質問

.NET の観点では:

  • とは何ですか メモリーリーク?
  • アプリケーションがリークしているかどうかをどうやって判断できるのでしょうか?どのような効果がありますか?
  • メモリリークを防ぐにはどうすればよいでしょうか?
  • アプリケーションにメモリ リークがある場合、プロセスが終了するか強制終了されるとメモリ リークは解消されますか?それとも、アプリケーションのメモリ リークは、プロセスの完了後でもシステム上の他のプロセスに影響を及ぼしますか?
  • COM Interop や P/Invoke 経由でアクセスされるアンマネージ コードはどうなるでしょうか?
役に立ちましたか?

解決

私が見た中で最も分かりやすい説明は、無料版の第 7 章にあります。 プログラミングの基礎電子書籍.

基本的には、 。ネット 参照されたオブジェクトがルート化されているため、ガベージ コレクションができない場合、メモリ リークが発生します。これは、意図したスコープを超えて参照を保持すると、誤って発生します。

OutOfMemoryExceptions が発生し始めたり、メモリ使用量が予想を超えて増加したりすると、リークがあることがわかります (パフォーマンスモン 優れたメモリカウンターがあります)。

理解 。ネットのメモリ モデルは、それを回避する最善の方法です。具体的には、ガベージ コレクターがどのように機能するか、参照がどのように機能するかを理解することについて、もう一度、電子書籍の第 7 章を​​参照してください。また、よくある落とし穴にも注意してください。おそらく最も一般的なのはイベントです。オブジェクトの場合 オブジェクトのイベントに登録されます B, 、その後、反対します オブジェクトが見つかるまでそこに留まります B 消えるから B への参照を保持します . 。解決策は、完了したらイベントの登録を解除することです。

もちろん、適切なメモリ プロファイルを使用すると、オブジェクト グラフを確認したり、オブジェクトのネスト/参照を調査して、参照がどこから来ているか、どのルート オブジェクトが責任を負っているかを確認できます (アカオアリのプロフィール, 、JetBrains ドットメモリ、 メンプロファイラー または、テキストのみを使用することもできます。 WinDbg そして SOS, ただし、本物の達人でない限り、商用/ビジュアル製品を強くお勧めします)。

私は、共有参照がガベージ コレクターによって管理されることを除いて、アンマネージ コードは典型的なメモリ リークの影響を受けると考えています。この最後の点については間違っている可能性があります。

他のヒント

厳密に言えば、メモリ リークは、プログラムによって「使用されなくなった」メモリを消費することです。

「使用されなくなった」には複数の意味があり、「参照がなくなった」、つまり完全に回復不可能であることを意味することもあれば、参照済み、回復可能、未使用であるがプログラムが参照を保持していることを意味することもあります。後者のみが .Net に適用されます。 完全に管理されたオブジェクト. 。ただし、すべてのクラスが完璧であるわけではなく、ある時点で、基盤となるアンマネージ実装により、そのプロセスのリソースが永久にリークされる可能性があります。

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

プロセスにますます多くのメモリが割り当てられていることが監視によって示されると、アプリケーションにメモリの問題があることがわかります。 各ガベージ コレクション サイクルの後. 。このような場合は、メモリ内に保持する量が多すぎるか、基盤となるアンマネージド実装のリークが発生しているかのどちらかです。

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

「メモリ リークとは何ですか」と「その影響は何ですか」という質問にはすでに十分に答えられていると思いますが、他の質問についてはさらにいくつか追加したいと思いました...

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

興味深い方法の 1 つは、次のように開くことです。 パフォーマンス そしてトレースを追加します すべてのヒープ内の # バイト そして # 第 2 世代コレクション 、いずれの場合も、プロセスだけを見ています。特定の機能を実行すると合計バイト数が増加し、そのメモリが次の Gen 2 コレクションの後も割り当てられたままになる場合は、その機能がメモリ リークを起こしていると言えるかもしれません。

予防方法

他にも良い意見が出されました。おそらく、 最も一般的に見落とされる .NET メモリ リークの原因は、オブジェクトを削除せずにオブジェクトにイベント ハンドラーを追加することです。オブジェクトにアタッチされたイベント ハンドラーは、そのオブジェクトへの参照の形式であるため、他のすべての参照がなくなった後でも収集が防止されます。常にイベント ハンドラーを切り離すことを忘れないでください ( -= C# の構文)。

プロセスが終了するとリークは解消されますか? COM 相互運用性はどうですか?

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

私はメモリ リークを、オブジェクトが完了した後に割り当てられたすべてのメモリを解放しないこととして定義します。Windows API と COM (つまり、フレームワークおよびサードパーティ コンポーネント内のアンマネージド コード (バグがあるか正しく管理されていないコード)。また、ペンなどの特定の物を使用した後に片付けていないことも問題の原因となる可能性があることがわかりました。

私は個人的に、ドット ネット アプリケーションでのメモリ リークに限定されないメモリ不足例外に悩まされました。(OOM はピン留めからも発生します。を参照してください) アーティカルの固定)。OOM エラーが発生していない場合、または原因がメモリ リークであるかどうかを確認する必要がある場合、唯一の方法はアプリケーションをプロファイリングすることです。

また、次のことも確認してみます。

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

b)closeメソッドを持つものはすべて、finallyまたはusingステートメントを使用して再度閉じられます(ただし、usingステートメントの外でオブジェクトを宣言したかどうかに応じて、usingが常に閉じないことがわかりました)

c) アンマネージ コード/Windows API を使用している場合、これらが後で正しく処理されること。(リソースを解放するためのクリーンアップ メソッドを備えたものもあります)

お役に立てれば。

.NET のメモリ リークを診断する必要がある場合は、次のリンクを確認してください。

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

これらの記事では、プロセスのメモリ ダンプを作成する方法とそれを分析する方法について説明します。これにより、リークが管理されていないものであるか管理されているのか、また管理されている場合はその原因を特定することができます。

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

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en

Microsoft の CLR Profiler の使用 http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=ja これは、どのオブジェクトがメモリを保持しているか、どのような実行フローによってこれらのオブジェクトが作成されるかを判断し、どのオブジェクトがヒープ上のどこに存在するか (断片化、LOH など) を監視する優れた方法です。

ガベージ コレクターがどのように機能するかについて最もよく説明されているのは、Jeff Richters です。 C# 経由の CLR 本、(Ch.20)。これを読むと、オブジェクトがどのように永続化されるかを理解するための優れた基礎が得られます。

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

例えば

SomeExternalClass.Changed += new EventHandler(HandleIt);

破棄するときにフックを解除するのを忘れると、SomeExternalClass にはクラスへの参照が含まれます。

上で述べたように、 SciTech メモリ プロファイラ は、漏れが疑われるオブジェクトの根元を表示するのに優れています。

ただし、WnDBG を使用するだけで特定の型を確認する非常に簡単な方法もあります (接続中に VS.NET イミディエイト ウィンドウでこれを使用することもできます)。

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

次に、そのタイプのオブジェクトを破棄すると思われる操作を実行します (例:窓を閉めてください)。ここで実行するデバッグ ボタンをどこかに置くと便利です。 System.GC.Collect() 数回。

それから実行します !dumpheap -stat -type <TypeName> また。数値が下がらなかった場合、または予想したほど下がらなかった場合は、さらに調査するための根拠が得られます。(このヒントは、あるセミナーから得ました) インゴ・ランマー).

管理された環境では、大量のメモリへの不必要な参照を保持していることがリークの原因になると思います。

.NET でのメモリ リークは他のリークと同じではないと考えるのはなぜですか?

メモリ リークは、リソースにアタッチしたまま放さない場合に発生します。これは、マネージド コーディングとアンマネージド コーディングの両方で実行できます。

.NET やその他のプログラミング ツールに関しては、ガベージ コレクションや、アプリケーションのリークを引き起こす状況を最小限に抑えるその他の方法に関するアイデアが存在します。しかし、メモリ リークを防ぐ最善の方法は、基礎となるメモリ モデルと、使用しているプラ​​ットフォームでの動作の仕組みを理解することです。

GC やその他の魔法で混乱が解消されると信じることはメモリ リークへの近道であり、後で見つけるのが難しくなります。

アンマネージでコーディングする場合、通常は必ずクリーンアップを行います。自分が保持しているリソースは、管理者の責任ではなく、自分の責任でクリーンアップする必要があることがわかっています。

一方、.NET では、GC がすべてをクリーンアップしてくれると多くの人が考えています。まあ、それはあなたにとってある程度の役に立ちますが、それが本当にそうなのかどうかを確認する必要があります。.NET は多くのものをラップしているため、マネージド リソースとアンマネージド リソースのどちらを扱っているのかが常にわかるとは限らず、何を扱っているのかを確認する必要があります。通常、フォント、GDI リソース、アクティブ ディレクトリ、データベースなどの処理に注意が必要です。

管理された言葉では、プロセスが殺され/削除されたら、首を首に置いて、それがなくなると言います。

しかし、多くの人がこのような状況に陥っているのを見ているので、これが終わることを本当に願っています。混乱を解消するためにアプリを終了するようにユーザーに要求することはできません。IE や FF などのブラウザを見て、たとえば Google Reader を開いて数日間放置し、何が起こるかを見てください。

次に、ブラウザで別のタブを開いて何らかのサイトにアクセスし、ブラウザのリークを引き起こした他のページをホストするタブを閉じた場合、ブラウザはメモリを解放すると思いますか?IEではそうではありません。私のコンピュータでは、Google Reader を使用すると、IE が短期間 (約 3 ~ 4 日) で 1 GiB のメモリを簡単に消費してしまいます。一部の新聞はさらにひどいです。

管理された環境では、漏れがあり、周りの大量のメモリへの不必要な参照を維持することです。

絶対に。また、必要に応じて、使い捨てオブジェクトに対して .Dispose() メソッドを使用しないと、メモリ リークが発生する可能性があります。これを行う最も簡単な方法は、using ブロックを使用することです。これは、最後に .Dispose() が自動的に実行されるためです。

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

また、アンマネージ オブジェクトを使用するクラスを作成する場合、IDisposable を正しく実装していないと、クラスのユーザーにメモリ リークが発生する可能性があります。

すべてのメモリ リークはプログラムの終了によって解決されます。

メモリが十分にリークすると、オペレーティング システムがユーザーに代わって問題を解決することを決定する可能性があります。

.net におけるメモリ リークがどのようなものであるかについては、Bernard の意見に同意します。

アプリケーションをプロファイリングしてメモリ使用量を確認し、管理すべきでないときに大量のメモリを管理している場合は、リークがあると判断できます。

マネージド用語では、プロセスが強制終了/削除されると問題はなくなると、私は身を賭して言います。

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

また、.NET には 2 つのヒープがあり、1 つはラージ オブジェクト ヒープであることにも注意してください。およそ 85k 以上のオブジェクトがこのヒープに置かれると思います。このヒープには、通常のヒープとは異なる有効期間ルールがあります。

大規模なメモリ構造 (辞書またはリスト) を作成している場合は、正確なルールが何であるかを調べてみるのが賢明です。

プロセスの終了時にメモリを再利用する限り、Win98 またはそれに相当するものを実行している場合を除き、終了時にすべてが OS に解放されます。唯一の例外は、プロセス間で開かれており、別のプロセスでまだリソースが開いているものです。

COM オブジェクトは難しい場合があります。いつも使っている場合は、 IDispose パターンなら安心ですよ。しかし、私は実装する相互運用アセンブリをいくつか見つけました。 IDispose. 。ここで重要なのは電話することです Marshal.ReleaseCOMObject それが終わったら。COM オブジェクトは引き続き標準の COM 参照カウントを使用します。

見つけました .Netメモリプロファイラー .Net でメモリ リークを見つけるときに非常に役立ちます。Microsoft CLR Profiler のように無料ではありませんが、私の意見では、より高速で、より要点を押さえています。あ

1 つの定義は次のとおりです。 到達不能なメモリを解放できません。割り当てプロセスの実行中に新しいプロセスに割り当てることができなくなります。ほとんどの場合、GC 技術を使用するか、自動ツールで検出することで修復できます。

詳細については、こちらをご覧ください。 http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top