165


65

NETパースペクティブでは
  • メモリリークとは何ですか?

  • アプリケーションがリークしているかどうかをどうやって判断できますか? 効果は何ですか?

  • どうすればメモリリークを防ぐことができますか?

  • アプリケーションにメモリリークがある場合、プロセスが終了するか強制終了されたときにそれが消えますか? それとも、アプリケーションのメモリリークは、プロセス完了後もシステム上の他のプロセスに影響を与えますか?

  • そして、COM相互運用機能やP / Invokeを介してアクセスされるアンマネージドコードはどうでしょうか。

15 Answer


110


私が見た最も良い説明は、無料の Programming of Programming e-bookの第7章にあります。

基本的に、* .NET *では、参照されているオブジェクトがルートであるためにメモリリークが発生するため、ガベージコレクトできません。 意図した範囲を超えた参照を握ると、これは偶然に起こります。

OutOfMemoryExceptionsが発生し始めたときや、メモリ使用量が予想以上に増えたときにリークが発生したことがわかります(* PerfMon *には素晴らしいメモリカウンタがあります)。

それを避けるための最善の方法は、* .NET のメモリモデルを理解することです。 具体的には、ガベージコレクタの仕組みと参照の仕組みを理解してください。ここでも、電子書籍の7章を参照してください。 また、よくある落とし穴、おそらく最も一般的な出来事に注意してください。 オブジェクト A がオブジェクト B のイベントに登録されている場合、 B A への参照を保持しているため、オブジェクト A はオブジェクト B *が消えるまで動き続けます。 解決策は、完了したらイベントの登録を解除することです。

もちろん、優れたメモリプロファイルを使用すると、オブジェクトグラフを表示したり、オブジェクトのネスティング/参照を調べて、参照元の場所や責任を持つルートオブジェクトを確認できます(http://www.red-gate.com/products)。 /ants_memory_profiler/index.htm [red -gate ants profile]、JetBrains dotMemory、 memprofilerは本当に良い選択です、またはテキストのみの* WinDbg SOS *を使うこともできますが、あなたが本物の教祖でない限り、私はコマーシャル/ビジュアル製品を強くお勧めします。

私は、管理されていないコードは、共有参照がガベージコレクタによって管理されることを除いて、その典型的なメモリリークの影響を受けやすいと思います。 この最後の点について私は間違っているかもしれません。


34


厳密に言うと、メモリリークはプログラムによって「使用されなくなった」メモリを消費しています。

「使用されなくなった」とは1つ以上の意味があり、「これ以上参照されていない」、つまり完全に回復不可能、または参照、回復可能、使用されていないことを意味します。 後者だけが*完全に管理されたオブジェクト*の.Netに適用されます。 ただし、すべてのクラスが完璧というわけではなく、ある時点で、基礎となる管理されていない実装がそのプロセスのために永続的にリソースをリークする可能性があります。

すべての場合において、アプリケーションは厳密に必要とされるよりも多くのメモリを消費します。 リークされた量に応じて、副作用は、なしから、過剰な収集によるスローダウン、一連のメモリ例外、そして最終的に致命的なエラーとそれに続く強制プロセス終了に至る可能性があります。

監視によって、各ガベージコレクションサイクル後*に、プロセスにより多くのメモリが割り当てられることが示されたときに、アプリケーションにメモリ問題があることがわかります。 そのような場合、あなたはメモリの中にあまりにも多くを保っているか、あるいは根本的な管理されていない実装がリークしています。

ほとんどのリークでは、プロセスが終了したときにリソースが回復されますが、正確な場合には常にリソースが回復されるわけではないため、GDIカーソルハンドルはそれで悪名高いです。 もちろん、プロセス間通信メカニズムがある場合、他のプロセスに割り当てられたメモリは、そのプロセスが解放するか終了するまで解放されません。


29


「メモリリークとは何ですか」と「効果は何ですか」という質問はすでによく回答されていると思いますが、他の質問にもいくつか追加したいと思います。

アプリケーションがリークしているかどうかを理解する方法

興味深い方法の1つは、perfmon_を開いて、すべてのヒープ_および#Gen 2コレクションに_#バイトのトレースを追加することです。 特定の機能を実行すると合計バイト数が増え、そのメモリが次のGen 2コレクションの後に割り当てられたままになっている場合は、その機能によってメモリリークが発生していると言えます。

防ぐ方法

他にも良い意見が出されています。 .NETメモリリークの最も一般的に見過ごされがちな原因は、イベントハンドラを削除せずにオブジェクトに追加することであると考えてみましょう。 オブジェクトにアタッチされているイベントハンドラはそのオブジェクトへの参照の形式であるため、他のすべての参照がなくなった後もコレクションを防止します。 常にイベントハンドラをデタッチすることを忘れないでください(C#の ` - =`構文を使用)。

プロセスが終了したときにリークは解消されますか。また、COM相互運用機能についてはどうですか?*

プロセスが終了すると、そのアドレス空間にマップされているすべてのメモリは、DLLから提供されるCOMオブジェクトも含めてOSによって回収されます。 比較的まれに、COMオブジェクトを別々のプロセスから提供することができます。 この場合、プロセスが終了しても、使用しているCOMサーバープロセスに割り当てられているメモリに対して責任がある可能性があります。


19


メモリリークは、完了後に割り当てられたメモリをすべて解放しないオブジェクトとして定義します。 Windows APIとCOMを使用している場合は、アプリケーションでこれが発生する可能性があります(例: バグがある、または正しく管理されていないアンマネージドコード、フレームワーク、およびサードパーティ製コンポーネント。 ペンのような特定のオブジェクトを使用した後に動かなくなっても問題が発生する可能性があることもわかりました。

私は個人的に引き起こされることができるがドットネットアプリケーションのメモリリークに排他的ではないメモリ不足例外を受けました。 (OOMはピン留めから来ることもできます Pinning Articalを参照してください)。 OOMエラーが発生していないか、それが原因でメモリリークが発生しているかどうかを確認する必要がある場合は、唯一の方法はアプリケーションのプロファイルを作成することです。

私も試してみて、次のことを確認します。

a)Idisposableを実装しているものはすべて、finallyブロックを使用するか、ブラシやペンなどのusingステートメントを使用して破棄されます(一部の人は、その他すべてに何も設定しないと主張します)。

b)closeメソッドを持つものは、finallyまたはusingステートメントを使用して再度閉じます(usingステートメントの外側でオブジェクトを宣言したかどうかによって、usingが閉じるとは限りませんが)。

c)管理されていないコード/ウィンドウズAPIを使用している場合、これらはその後正しく処理されます。 (リソースを解放するためのメソッドを整理するものもあります)

お役に立てれば。


19


NETでメモリリークを診断する必要がある場合は、次のリンクを確認してください。
https://web.archive.org/web/20141203155344/http://msdn.microsoft.com/ja-jp/magazine/cc163833.aspx[http://msdn.microsoft.com/ja-jp/magazine/ cc163833.aspx]
https://web.archive.org/web/20150102222036/http://msdn.microsoft.com/ja-jp/magazine/cc164138.aspx[http://msdn.microsoft.com/ja-jp/magazine/ cc164138.aspx]

これらの記事では、プロセスのメモリダンプを作成する方法とそれを分析してリークが管理されていないのか、管理されているのかを判断できるように分析する方法について説明しています。

Microsoftには、ADPlusに代わる、クラッシュダンプの生成を支援するDebugDiagという新しいツールもあります。


15


MicrosoftからのCLRプロファイラーの使用http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda


15


ガベージコレクタがどのように機能するかについての最も良い説明は、Jeff Richtersのhttps://www.microsoftpressstore.com/store/clr-via-c -sharp-9780735667457 [C#via via C#]の本にあります。 20). これを読むことは、オブジェクトがどのように存続するかを理解するための大きな根拠を与えます。

オブジェクトを誤ってルート化する最も一般的な原因の1つは、クラス外のイベントをフックすることです。 外部イベントを接続した場合

e.g.

SomeExternalClass.Changed += new EventHandler(HandleIt);

そして、破棄するときにフックを外すのを忘れてください。そうすればSomeExternalClassはあなたのクラスへの参照を持ちます。

上記のように、http://memprofiler.com [SciTech memory profiler]は、あなたがリークしている疑いのあるオブジェクトのルーツをあなたに示すのに優れています。

しかし、WnDBGを使用するだけで特定のタイプを確認するための非常に素早い方法もあります(アタッチされている間にVS.NETのイミディエイトウィンドウでこれを使用することもできます)。

.loadby sos mscorwks
!dumpheap -stat -type

さて、あなたがそのタイプのオブジェクトを処分すると思う何かをしてください。 ウィンドウを閉じます。 System.GC.Collect()を何度か実行するデバッグボタンをどこかに置くと便利です。

それから `!dumpheap -stat -type`を再度実行してください。 数が減っていなかったり、予想したほど減っていなかった場合は、さらなる調査の基礎があります。 (私はこのヒントをhttp://www.thinktecture.com/staff/ingo [Ingo Rammer]によるセミナーから得ました)。


14


管理された環境では、リークはあなたが周りの大きなメモリの塊を不必要に参照し続けることになるでしょう。


11


NETのメモリリークは他のリークと同じではないと人々が考えるのはなぜですか。

メモリリークは、リソースにアタッチして、リソースを解放しないことです。 これはマネージコーディングでもアンマネージドコーディングでも実行できます。

NETや他のプログラミングツールに関しては、ガベージコレクションや、アプリケーションがリークする可能性がある状況を最小限に抑えるその他の方法についてのアイデアがあります。 しかし、メモリリークを防ぐための最善の方法は、使用しているプラ​​ットフォーム上で、基礎となるメモリモデル、および機能の仕組みを理解する必要があることです。

GCや他の魔法があなたの混乱を一掃すると信じることはメモリリークへの短い方法であり、後で見つけるのは難しいでしょう。

管理されていないコーディングをするときは、通常、クリーンアップすることを忘れないでください。あなたが保持しているリソースは、用務員ではなく、クリーンアップする責任です。

一方、.NETでは、GCがすべてをクリーンアップすると多くの人が考えています。 まあ、それはあなたのためにいくつかを行いますが、あなたはそれがそうであることを確認する必要があります。 .NETは多くのことをラップするので、あなたが管理されているか管理されていないリソースを扱っているかを常に知っているわけではないので、あなたが扱っているものを確かめる必要があります。 フォント、GDIリソース、Active Directory、データベースなどの処理は、通常あなたが注意を払う必要があるものです。

管理された言葉で言えば、プロセスが殺されたり取り除かれたりしたらそれは消えると言うために、私は首に線を引くつもりです。

私は多くの人がこれを持っているのを見ます、そして私は本当にこれが終わることを願っています。 あなたはあなたの混乱をきれいにするためにあなたのアプリを終了するようにユーザに頼むことはできません! IE、FFなどのブラウザを見て、開いて、たとえば、Googleリーダーを開いて、数日間そのままにしておき、何が起こるのか見てみましょう。

その後、ブラウザで別のタブを開いて、あるサイトにアクセスし、ブラウザをリークさせた他のページをホストしていたタブを閉じた場合、ブラウザはメモリを解放すると思いますか? IEではそうではありません。 私のコンピュータでは、Google Readerを使用すれば、IEは短期間(約3〜4日)で1 GiBのメモリを簡単に食べることができます。 いくつかのニュースページはさらに悪いです。


10


私はBernardに.netでのmemリークがどうなるかについて同意します。

あなたはあなたのアプリケーションがそのメモリ使用量を見るようにプロファイルすることができ、そしてそれがそうであるべきではないときに大量のメモリを管理しているならあなたはそれがリークを持っていると言えるでしょう。

管理された言葉で、私はプロセスが殺された/削除されたらそれが消えると言うためにラインに私の首を置くでしょう。

アンマネージコードはそれ自身の獣であり、その中にリークが存在する場合、それは標準のmemに従います。 リーク定義