38

Windowsフォームアプリケーションでメモリリークを実行しようとしています。私は今、いくつかの埋め込まれたフォームを含むフォームを見ています。私が心配しているのは、子フォームがコンストラクターで親フォームへの参照を取得し、それをプライベートメンバーフィールドに保持することです。ですから、ガベージコレクションの時期が来ると私には思えます。

親は、controlsコレクションを介して子フォームへの参照を持っています(子フォームはそこに埋め込まれています)。子フォームはGCされません。

子フォームには、プライベートメンバーフィールドを介した親フォームへの参照があります。親フォームはGCされません。

これは、ガベージコレクターが状況を評価する方法を正確に理解していますか?テスト目的でそれを「証明」する方法はありますか?

4

6 に答える 6

44

素晴らしい質問です!

いいえ、GCは他の参照内の参照を直接検索しないため、両方の形式がGCされます(可能性があります)。「ルート」参照と呼ばれるもののみを検索します...これには、スタック上の参照変数(変数はスタック上にあり、実際のオブジェクトはもちろんヒープ上にあります)、CPUレジスタ内の参照変数、および次の参照変数が含まれます。クラスの静的フィールド...

他のすべての参照変数は、上記のプロセスで見つかった「ルート」参照オブジェクトの1つのプロパティで参照されている場合(またはルートオブジェクトの参照によって参照されているオブジェクトで参照されている場合)にのみアクセス(およびGC)されます。 、など...)

したがって、フォームの1つが「ルート」参照内の別の場所で参照されている場合にのみ、両方のフォームがGCから安全になります。

(メモリトレースユーティリティを使用せずに)それを「証明」するために考えることができる唯一の方法は、メソッド内のループでこれらのフォームを数十万個作成し、メソッド内でアプリのメモリフットプリントを確認することです。 、次にメソッドを終了し、GCを呼び出して、フットプリントをもう一度確認します。

于 2008-12-30T16:23:15.540 に答える
15

他の人がすでに言ったように、GC には循環参照の問題はありません。.NET でメモリ リークが発生する一般的な場所はイベント ハンドラーであることを付け加えておきます。フォームの 1 つに「生きている」別のオブジェクトへのイベント ハンドラーが添付されている場合、フォームへの参照があり、フォームは GC されません。

于 2008-12-30T16:45:21.843 に答える
12

ガベージ コレクションは、アプリケーションのルートを追跡することによって機能します。アプリケーション ルートは、マネージ ヒープ上のオブジェクト (または null) への参照を含むストレージの場所です。.NET では、ルートは

  1. グローバル オブジェクトへの参照
  2. 静的オブジェクトへの参照
  3. 静的フィールドへの参照
  4. スタック上のローカル オブジェクトへの参照
  5. メソッドに渡されるオブジェクト パラメーターへのスタック上の参照
  6. ファイナライズ待ちのオブジェクトへの参照
  7. マネージ ヒープ上のオブジェクトへの CPU レジスタ内の参照

アクティブなルートのリストは、CLR によって維持されます。ガベージ コレクターは、マネージ ヒープ上のオブジェクトを調べて、アプリケーションからまだアクセスできるオブジェクト (つまり、アプリケーション ルート経由でアクセスできるオブジェクト) を確認することによって機能します。このようなオブジェクトは、ルート化されていると見なされます。

ここで、子フォームへの参照を含む親フォームがあり、これらの子フォームに親フォームへの参照が含まれているとします。さらに、アプリケーションには、親フォームまたは子フォームのいずれかへの参照が含まれていないとします。次に、ガベージ コレクターの目的で、これらの管理対象オブジェクトはルート化されなくなり、次にガベージ コレクションが発生したときにガベージ コレクションが行われます。

于 2008-12-30T16:48:22.680 に答える
5

親と子の両方が参照されていないが、相互に参照しているだけの場合、GCされます。

メモリプロファイラーを入手して、アプリケーションを実際にチェックし、すべての質問に答えてください。http://memprofiler.com/をお勧めします

于 2008-12-30T16:23:55.663 に答える
2

イベントに関する Vilx の発言に同意し、それに対処するのに役立つ設計パターンを推奨したいと思います。

たとえば、イベント ソースである型があるとします。

interface IEventSource
{
    event EventHandler SomethingHappened;
}

そのタイプのインスタンスからのイベントを処理するクラスのスニペットを次に示します。プロパティに新しいインスタンスを割り当てるときはいつでも、最初に以前の割り当てからサブスクライブを解除してから、新しいインスタンスをサブスクライブするという考え方です。null チェックにより、正しい境界の動作が保証され、さらに重要なことに、破棄が単純化されます。プロパティを null にするだけです。

それは処分のポイントをもたらします。イベントはマネージ リソースであるため、イベントをサブスクライブするすべてのクラスは IDisposable インターフェイスを実装する必要があります。(注: 簡潔にするために、この例では Dispose パターンの適切な実装を省略しましたが、おわかりいただけると思います。)

class MyClass : IDisposable
{
    IEventSource m_EventSource;
    public IEventSource EventSource
    {
        get { return m_EventSource; }
        set
        {
            if( null != m_EventSource )
            {
                m_EventSource -= HandleSomethingHappened;
            }
            m_EventSource = value;
            if( null != m_EventSource )
            {
                m_EventSource += HandleSomethingHappened;
            }
        }
    }

    public Dispose()
    {
        EventSource = null;
    }

    // ...
}
于 2011-06-24T20:34:11.587 に答える
0

GC は循環参照を正しく処理できます。これらの参照がフォームを維持する唯一のものである場合、それらの参照は収集されます。
.net がフォームからメモリを再利用しないという問題がたくさんありました。1.1 では、menuitem の周りにいくつかのバグがあり (私が思うに)、破棄されず、メモリ リークが発生する可能性がありました。この場合、dispose への明示的な呼び出しを追加し、フォームの Dispose メソッドでメンバー変数をクリアすると、問題が解決されました。これは、他のいくつかのコントロール タイプのメモリの再利用にも役立つことがわかりました。
また、CLR プロファイラーを長時間使用して、フォームが収集されない理由を調べました。私の知る限り、参照はフレームワークによって保持されていました。フォームの種類ごとに 1 つ。したがって、Form1 のインスタンスを 100 個作成し、それらをすべて閉じると、99 個だけが適切に再利用されます。これを治す方法が見つかりませんでした。
その後、アプリケーションは .net 2 に移行しましたが、これははるかに優れているようです。アプリケーション メモリは、最初のフォームを開いたときに増加し、閉じても元に戻りませんが、これは、JIT されたコードと追加のコントロール ライブラリが読み込まれているためだと思います。
また、GC は循環参照を処理できますが、(時々) 循環イベント ハンドラー参照に問題があるようです。IE object1 は object2 を参照し、object1 には object2 からのイベントを処理するメソッドがあります。これで期待どおりにオブジェクトが解放されない状況が見つかりましたが、テスト ケースでそれを再現することはできませんでした。

于 2008-12-30T16:38:16.383 に答える