アプリケーションで大きなサイズのオブジェクト (>85000 バイト) の割り当て/割り当て解除を頻繁に行う必要がある場合、最終的にはメモリの断片化が発生し、アプリケーションはメモリ不足の例外をスローします。
この問題の解決策はありますか、それとも CLR メモリ管理の制限ですか?
アプリケーションで大きなサイズのオブジェクト (>85000 バイト) の割り当て/割り当て解除を頻繁に行う必要がある場合、最終的にはメモリの断片化が発生し、アプリケーションはメモリ不足の例外をスローします。
この問題の解決策はありますか、それとも CLR メモリ管理の制限ですか?
残念ながら、私が今まで見たすべての情報は、リスク要因を自分で管理することを示唆しているだけです。大きなオブジェクトを再利用し、最初に割り当て、互いに倍数のサイズであることを確認し、代替のデータ構造(リスト、ツリー)を使用します。配列の代わりに。これで、1つの大きな配列の代わりに、小さな配列に分割される非断片化リストを作成するという別のアイデアが得られました。配列/リストは、IMEの最も頻繁な原因のようです。
これに関するMSDNマガジンの記事は次のとおりです。http: //msdn.microsoft.com/en-us/magazine/cc534993.aspxですが、それほど有用ではありません。
CLR のガベージ コレクター内の大きなオブジェクトに関する問題は、それらが別のヒープで管理されることです。ガベージ コレクターは、「圧縮」と呼ばれるメカニズムを使用します。これは、基本的に、通常のヒープ内のオブジェクトの断片化と再結合です。問題は、大きなオブジェクトを「圧縮」する (コピーして再リンクする) のはコストのかかる手順であるため、GC はそれらに別のヒープを提供し、圧縮されることはありません。
メモリ割り当てが連続していることにも注意してください。つまり、オブジェクト #1 を割り当ててからオブジェクト #2 を割り当てた場合、オブジェクト #2 は常にオブジェクト #1 の後に配置されます。
これがおそらく、OutOfMemoryExceptions が発生する原因です。
Flyweight、Lazy Initialization、Object Pool などのデザイン パターンを検討することをお勧めします。
これらの大きなオブジェクトの一部がすでに死んでいて、制御フローの欠陥のために収集されていないことが疑われる場合は、GC コレクションを強制することもできます。これにより、コレクションの準備が整う直前に、それらがより高い世代に到達します。
プログラムが常に OOM を爆撃するのは、大きすぎるメモリのチャンクを要求しているためであり、すべての仮想メモリ アドレス空間を完全に使い果たしたからではありません。これは LOH が断片化する問題であると主張できますが、プログラムが仮想メモリを使いすぎていると主張するのも同じくらい簡単です。
プログラムがアドレス指定可能な仮想メモリの半分 (1 ギガバイト) を割り当てられなくなったら、メモリをあまり消費しないようにコードをよりスマートにすることを検討する時期です。または、64 ビット オペレーティング システムを前提条件にします。後者は常に安価です。それはあなたのポケットからも出てきません。
Is there any solution to this problem or is it a limitation of CLR memory management?
デザインを再考する以外に解決策はありません。そして、それはCLRの問題ではありません。管理されていないアプリケーションでも問題は同じであることに注意してください。これは、アプリケーションによって同時に、またメモリ内に「不利」な部分を配置するセグメントで使用されるメモリが多すぎるという事実によって示されます。それでも外部の原因を指摘する必要がある場合は、(もちろん)VMアドレス空間を圧縮しないOSメモリマネージャーを指摘したいと思います。
CLRは、LOHの空き領域をフリーリストで管理します。ほとんどの場合、これは断片化に対して実行できる最善の方法です。ただし、非常に大きなオブジェクトの場合、LOHセグメントあたりのオブジェクト数が減少するため、最終的にはセグメントあたり1つのオブジェクトしかなくなります。そして、それらのオブジェクトがvmスペースに配置される場所は、完全にOSのメモリマネージャー次第です。つまり、断片化は主にOSレベルで発生し、CLRでは発生しません。これはヒープの断片化のよく見られる側面であり、それを非難するのは.NETではありません。(しかし、それはまた真実であり、その記事でうまく示されているように、断片化は管理された側でも発生する可能性があります。)
一般的なソリューションにはすでに名前が付けられています。大きなオブジェクトを再利用します。私はこれまで、適切な設計ではこれを実行できない状況に直面していませんでした。ただし、それは時々トリッキーになる可能性があり、したがって高価になる可能性があります。
別の回答で、LOH のサイズが縮小する可能性があることを見てきました。
大規模な配列、および LOH フラグメンテーション。受け入れられている規則は何ですか?
" ... そうは言っても、LOH の最後の領域にライブ オブジェクトが完全にない場合、LOH のサイズが縮小する可能性があるため、唯一の問題は、そこにオブジェクトを長時間放置した場合です (たとえば、応用)。 ... "
それ以外では、32 ビット システムで最大 3 GB、64 ビット システムで最大 4 GB の拡張メモリを使用してプログラムを実行できます。リンカーまたはこのビルド後のイベントに /LARGEADDRESSAWARE フラグを追加するだけです。
"$(DevEnvDir)..\tools\vsvars32.bat" editbin /LARGEADDRESSAWARE "$(TargetPath)" を呼び出します
最後に、多くの大きなオブジェクトでプログラムを長時間実行することを計画している場合は、メモリ使用量を最適化する必要があり、割り当てられたオブジェクトを再利用して、概念が似ているガベージ コレクターを回避する必要がある場合もあります。リアルタイムシステム。