web-dev-qa-db-ja.com

イベントハンドラのメモリリークを回避する理由と方法

StackOverflowに関する質問と回答を読んで、C#(または他の.net言語)で+=を使用してイベントハンドラーを追加すると、一般的なメモリリークが発生する可能性があることに気付きました...

過去に何度もこのようなイベントハンドラを使用してきましたが、アプリケーションでメモリリークが発生する可能性があること、またはメモリリークが発生することはありませんでした。

これはどのように機能しますか(つまり、なぜこれが実際にメモリリークを引き起こすのですか)?
この問題を修正するにはどうすればよいですか? -=を同じイベントハンドラに十分に使用していますか?
このような状況を処理するための一般的な設計パターンやベストプラクティスはありますか?
例:さまざまなイベントハンドラーを使用してUIでいくつかのイベントを発生させる、さまざまなスレッドを持つアプリケーションをどのように処理するのですか?

すでに構築された大きなアプリケーションでこれを効率的に監視するための良い簡単な方法はありますか?

149
gillyb

原因は簡単に説明できます。イベントハンドラーがサブスクライブされている間、イベントのpublisherは、イベントハンドラーデリゲートを介してsubscriberへの参照を保持します(デリゲートがインスタンスメソッド)。

パブリッシャーがサブスクライバーより長く存続する場合、サブスクライバーへの他の参照がない場合でも、サブスクライバーは存続します。

等しいハンドラーを使用してイベントのサブスクライブを解除すると、はい、ハンドラーと潜在的なリークが削除されます。ただし、私の経験では、これが実際に問題になることはめったにありません。通常、パブリッシャーとサブスクライバーの寿命はほぼ同じであることがわかっています。

それはis考えられる原因です...しかし、私の経験ではかなり誇張されています。もちろん、走行距離は異なる場合があります...注意する必要があります。

185
Jon Skeet

はい、 -=で十分ですが、割り当てられたすべてのイベントを追跡するのは非常に困難です。 (詳細については、ジョンの投稿を参照してください)。デザインパターンについては、 弱いイベントパターン をご覧ください。

12
Femaref

イベントは実際にはイベントハンドラーのリンクリストです

イベントで+ = new EventHandlerを実行する場合、この特定の関数が以前にリスナーとして追加されていても、実際には問題ではありません。+ =ごとに1回追加されます。

イベントが発生すると、アイテムごとにリンクされたリストを通過し、このリストに追加されたすべてのメソッド(イベントハンドラー)を呼び出します。生きている(ルート化されている)もので、接続されている限り生き続けます。そのため、イベントハンドラが-= new EventHandlerでアンフックされるまで呼び出されます。

こちらを参照

および MSDNこちら

3
TalentTuner