20

.NET でメモリ リークが発生する可能性のあるすべての方法は何ですか?

私は2つ知っています:

  1. Event Handlers/Delegatesの登録が適切に解除されていません。
  2. Windows フォームで動的な子コントロールを破棄しない:

例:

// Causes Leaks  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// Correct Code  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

更新:アイデアは、あまり明白ではない一般的な落とし穴(上記など)をリストすることです。通常、ガベージ コレクターのおかげで、メモリ リークは大きな問題ではないと考えられています。以前の C++ とは異なります。


素晴らしいディスカッションですが、明確にさせてください... 定義により、.NET にオブジェクトへの参照が残っていない場合、それはいつかガベージ コレクションになります。したがって、これはメモリ リークを誘発する方法ではありません。

マネージド環境では、認識していないオブジェクトへの意図しない参照があった場合、メモリ リークと見なします (したがって、私の質問の 2 つの例)。

では、このようなメモリ リークが発生する可能性のあるさまざまな方法にはどのようなものがあるでしょうか。

4

14 に答える 14

21

これは実際にリークを引き起こすわけではなく、GC の作業が増えるだけです。

// slows GC
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  

// better  
Label label = new Label();  
this.Controls.Add(label);  
this.Controls.Remove(label);  
label.Dispose();

// best
using( Label label = new Label() )
{ 
    this.Controls.Add(label);  
    this.Controls.Remove(label);  
}

.Net のような管理された環境では、使い捨てコンポーネントをこのように放置しておくことは、決して大きな問題ではありません。これは、管理が意味するものの大きな部分です。

確かに、アプリの速度が低下します。しかし、あなたは他の何かのために混乱を残すことはありません.

于 2008-08-21T16:21:22.623 に答える
14

BindingSource クラス ( http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx ) のインスタンスを使用せずにGridControl.DataSourceプロパティを直接設定します。

これにより、アプリケーションでリークが発生し、プロファイラーで追跡するのにかなりの時間がかかりました。最終的に、Microsoft が回答した次のバグ レポートを見つけました。 92260

Microsoft が BindingSource クラスのドキュメントで、よく考え抜かれた正当なクラスとしてそれを広めようとしているのは面白いですが、通貨マネージャーとグリッド コントロールへのデータのバインドに関する根本的なリークを解決するために作成しただけだと思います。

これには気をつけてください。このため、リークのあるアプリケーションが絶対にたくさんあるに違いありません!

于 2008-11-25T20:34:06.973 に答える
14

包括的なリストを提供する方法はありません...これは、「どうすれば濡れますか?」と尋ねるようなものです。

つまり、IDisposable を実装するすべてのもので Dispose() を呼び出していることを確認し、あらゆる種類のアンマネージ リソースを消費するすべての型で IDisposable を実装していることを確認してください。

ときどき、コードベースで FxCop のようなものを実行して、そのルールを適用できるようにします。使い捨てオブジェクトがアプリケーション フレームワーク内にどれほど深く埋め込まれているかに驚かれることでしょう。

于 2008-08-21T16:13:22.720 に答える
5

ファイナライザー スレッドをブロックします。ファイナライザー スレッドのブロックが解除されるまで、他のオブジェクトはガベージ コレクションされません。したがって、使用されるメモリの量はどんどん増えていきます。

さらに読む: http://dotnetdebug.ne​​t /2005/06/22/blocked-finalizer-thread/

于 2008-08-23T08:17:01.867 に答える
4

アンマネージ リソースが正しく破棄されないようにする Finalize (または Finaliser からの Dispose 呼び出し) メソッドの例外。よくあるのは、プログラマーがオブジェクトが破棄される順序を想定し、既に破棄されているピア オブジェクトを解放しようとした結果、例外が発生し、Finalize メソッドからの Finalise/Dispose メソッドの残りの部分が呼び出されなかったことが原因です。

于 2008-08-21T16:38:56.030 に答える
3

この議論に追加する項目が 4 つあります。

  1. このようなイベントを適切に準備せずに UI コントロールを作成したスレッド (Thread.Abort()) を終了すると、メモリが予期せず使用される可能性があります。

  2. Pinvoke を介してアンマネージ リソースにアクセスし、それらをクリーンアップしないと、メモリ リークが発生する可能性があります。

  3. 大きな文字列オブジェクトの変更。必ずしもメモリ リークではありません。範囲外になると、GC が処理しますが、パフォーマンスに関しては、大きな文字列が頻繁に変更されると、システムが打撃を受ける可能性があります。これは、プログラムのフット プリントを確保するために GC に実際に依存することができないためです。最小限。

  4. カスタム描画を実行するために頻繁に GDI オブジェクトを作成します。GDI 作業を頻繁に実行する場合は、単一の gdi オブジェクトを再利用します。

于 2010-07-27T18:25:47.947 に答える
2

.NET メモリ リークを防ぐには:

1) 'IDisposable' インターフェイスを持つオブジェクトが作成されるときは常に、'using' コンストラクト (または 'try-finally コンストラクト) を使用します。

2) クラスがスレッドを作成する場合、またはオブジェクトを静的コレクションまたは長期存続コレクションに追加する場合は、クラスを「IDisposable」にします。C# の「イベント」はコレクションであることを思い出してください。

これは、メモリ リークを防ぐためのヒントに関する短い記事です。

于 2010-06-25T20:20:29.487 に答える
2

毎回 IDisposable を呼び出すことは、開始するのが最も簡単な場所であり、コードベースで簡単に実行できるメモリ リークの成果をすべて把握する効果的な方法であることは間違いありません。ただし、常に十分であるとは限りません。たとえば、マネージ コードが実行時に生成される方法とタイミング、およびアセンブリがアプリケーション ドメインに読み込まれるとアンロードされないことを理解することも重要です。これにより、アプリケーションのフットプリントが増加する可能性があります。

于 2008-08-21T16:19:17.450 に答える
2

予期しないメモリ使用量または実際のリークについて話しているのですか? リストした 2 つのケースは正確にはリークではありません。それらは、オブジェクトが意図したよりも長く留まる場合です。

つまり、メモリ リークと呼ぶ人が知らなかった、または忘れていたリファレンスです。

編集:または、ガベージコレクターまたは非管理コードの実際のバグです。

編集 2: これについて考えるもう 1 つの方法は、オブジェクトへの外部参照が適切に解放されることを常に確認することです。外部とは、制御できないコードを意味します。それが発生する場合は、メモリを「リーク」する可能性がある場合です。

于 2008-08-21T16:18:12.397 に答える
1

私にとって本当に予想外だったことの1つは、これです。

Region oldClip = graphics.Clip;
using (Region newClip = new Region(...))
{
    graphics.Clip = newClip;
    // draw something
    graphics.Clip = oldClip;
}

メモリリークはどこにありますか?そうです、あなたも処分すべきだったのoldClipです!Graphics.Clipこれは、ゲッターが呼び出されるたびに新しい使い捨てオブジェクトを返すまれなプロパティの1つだからです。

于 2010-06-25T20:30:16.290 に答える
1

Tess Fernandez は、メモリ リークの検出とデバッグに関するすばらしいブログ記事を投稿しています。 ラボ 6 ラボ 7

于 2010-07-28T13:46:03.563 に答える
1
  1. 不要になったオブジェクトへの参照を保持します。

他のコメントについて - Dispose が確実に呼び出されるようにする 1 つの方法は、コード構造で許可されている場合に using... を使用することです。

于 2009-11-13T01:20:48.917 に答える
0

アンマネージド言語でメモリ リークを引き起こす可能性のある多くのことは、マネージド言語でもメモリ リークを引き起こす可能性があります。たとえば、不適切なキャッシング ポリシーは、メモリ リークを引き起こす可能性があります。

しかし、グレッグとダニーが言ったように、包括的なリストはありません。有効期間が経過した後にメモリを保持する可能性があるものはすべて、リークを引き起こす可能性があります。

于 2008-08-21T16:18:28.387 に答える
0

デッドロックされたスレッドは決してルートを解放しません。明らかに、デッドロックがより大きな問題を引き起こしていると主張することができます。

デッドロックされたファイナライザー スレッドは、残りのすべてのファイナライザーの実行を妨げ、その結果、すべてのファイナライズ可能なオブジェクトが再利用されなくなります (それらはまだ freachable リストによってルート化されているため)。

マルチ CPU マシンでは、ファイナライザー スレッドがファイナライザーを実行するよりも速く、ファイナライズ可能なオブジェクトを作成できます。それが持続している限り、メモリを「リーク」します。これが実際に発生する可能性はあまり高くありませんが、簡単に再現できます。

大きなオブジェクト ヒープは圧縮されないため、断片化によってメモリ リークが発生する可能性があります。

手動で解放しなければならないオブジェクトが多数あります。たとえば、リースとアセンブリのないリモート オブジェクト (AppDomain をアンロードする必要があります)。

于 2008-11-25T20:08:21.643 に答える