2
void Foo()
{
     System.Windows.Forms.Form f = new System.Windows.Forms.Form();
     f.Show();
}

私の理解では、fはフォームへの参照を保持しています。ただし、fはローカル変数であり、コントロールが中括弧を離れるとスコープ外になります。しかし、フォームはまだ開いています。GC.Collect()を呼び出してみましたが、フォームはまだ開いています。

もう1つのシナリオ。

private void button2_Click(object sender, EventArgs e)
    {
        Timer t = new Timer();
        t.Enabled = true;
        t.Interval = 1000;
        t.Tick += new EventHandler(t_Tick);
    }

    void t_Tick(object sender, EventArgs e)
    {

    }

このシナリオでは、tがガベージコレクションを取得することはありません。多くの調査の結果、t.Enabled = trueに設定すると、Timerクラスが-GCHandle.Allocを使用して収集しないようにGCに要求していることがわかりました。皆さん、これはメモリリークの大きな原因です。t.Enabled = falseに設定しない限り、フォームを閉じた後でもフォーム全体がリークされます。

最初のサンプルコードでは、GC.Collect()をトリガーした後でも、フォームがガベージコレクションを取得しなかった理由を理解できませんでした。リフレクターでは、ControlNativeWindowがGCHandle.Allocを内部的に使用するFormで使用されているのを見ました。それが理由ですか?.NETライブラリのユーザーとして、私は常に、参照に到達できなくなると、ガベージコレクションの機会が得られると信じていました。もちろん、ガベージコレクションとメモリからの実際の解放は決定論的ではありません。しかし、ここでの私の質問は-両方の例について私の理解は正しいですか?到達不能になった後も存続できるオブジェクトがある場合、メモリリークを防ぐためにそれをどのように追跡しますか?

4

2 に答える 2

4

Winformsは、ハンドルをコントロールインスタンスにマップする内部テーブルを保持します。このテーブルにより、ネイティブウィンドウが有効である限り、コントロール(この場合はフォーム)がガベージコレクションされないことが保証されます。ユーザーがフォームを閉じるか、コードでフォームを破棄することにより、ウィンドウが破棄されると、そのテーブルから削除されます。

System.Timers.Timerは、CLRによって参照されるCookieによって存続します。このクラスは、状態オブジェクトの引数を取るコンストラクターを持つSystem.Threading.Timerを使用して実装されます。その状態オブジェクトはCookieであり、CLRはそれをGCHandle.Alloc()と同等のもので参照し続けます。タイマーを無効にするとCookieがリセットされ、タイマーをガベージコレクションできるようになります。

これらは、フレームワークがこれらのオブジェクトがガベージコレクションを早すぎるのを防ぐための自然で必要な方法です。フォームが破棄されるときにタイマーを無効にするのを忘れた場合にのみ、リークが発生する可能性があります。これは一般的に非常に不健康ですが、フォームが死んでいるときにタイマーがカチカチ音をたて続けるのは望ましくありません。DisposeメソッドをDesigner.csファイルからフォームコードに移動するか、OnFormClosed()をオーバーライドしてタイマーを無効にします。

于 2013-01-31T02:27:52.403 に答える
1

Formインスタンスの存続期間とインスタンスを自分で管理する必要がありSystem.Windows.Forms.Timerます。Disposeあなたは彼らの生涯の終わりをマークするために電話する必要があり、GC後でそれらを集めるでしょう。フォームを閉じると、Dispose内部で呼び出されます。Designerを使用してタイマーがフォームに配置された場合、フォームが閉じられたときにタイマーが破棄されます。これは、設計者がフォームコンストラクターで次のコードを生成することによって実現されます。

this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);

Dispose形式は次のとおりです。

protected override void Dispose(bool disposing)
{
    if (disposing && (components != null))
    {
        components.Dispose();
    }
    base.Dispose(disposing);
}

したがって、実際には、フォームを非表示にするだけでなく、フォームを閉じる限り、メモリリークは発生しません。ただし、タイマーを手動で作成した場合は、フォームを閉じたときに自分でタイマーを破棄する必要があります。

一方、System.Threading.Timer参照がない場合でも、参照がない場合はガベージコレクションが行われますDispose

于 2013-01-31T02:28:13.823 に答える