配列のガベージ コレクションを強制する、C#
-
12-09-2019 - |
質問
いくつかの 3 次元配列が大量のメモリを割り当て、プログラムがそれらをより大きい/より小さい配列に置き換える必要があり、OutOfMemoryException をスローするという問題があります。
例:5 つの割り当てられた 96MB 配列 (200x200x200、各エントリに 12 バイトのデータ) があり、プログラムはそれらを 210x210x210 (111MB) に置き換える必要があります。これは次のような方法で行われます。
array1 = new Vector3[210,210,210];
ここで、array1 ~ array5 は以前に使用したものと同じフィールドです。これにより、古い配列がガベージ コレクションの候補として設定されるはずですが、GC が十分に迅速に動作せず、新しい配列を割り当てる前に古い配列が割り当てられたままになるため、OOM が発生します。一方、新しい割り当ての前に解放された場合、スペースは確保されるはずです。十分。
私が探しているのは、次のようなことを行う方法です。
GC.Collect(array1) // this would set the reference to null and free the memory
array1 = new Vector3[210,210,210];
そのコードは (状況によっては) かなり頻繁に実行する必要があるため、フル ガベージ コレクションが良いアイデアかどうかはわかりません。
これを行う適切な方法はありますか?
解決
これは、元の質問「GC を強制する方法」に対する正確な答えではありませんが、問題を再検討するのに役立つと思います。
あなたのコメントを見てから、
- GC.Collect(); を配置します。問題を完全に解決するわけではありませんが、役立つようです。約 1.3 GB が割り当てられると、何らかの理由でプログラムがクラッシュします (System.GC.GetTotalMemory( false ); を使用しています)。実際に割り当てられた量を確認します)。
あなたがそうしているのではないかと疑ってしまいます 記憶の断片化. 。オブジェクトが大きい場合 (私の記憶が正しければ .net 2.0 CLR で 85000 バイト、変更されたかどうかはわかりません)、オブジェクトは特別なヒープに割り当てられます。 ラージ オブジェクト ヒープ (LOH). 。GC は、LOH 内の到達不能なオブジェクトによって使用されているメモリを再利用しますが、 圧縮は実行されません。 LOH では、パフォーマンス上の理由から、他のヒープ (gen0、gen1、gen2) と同様に実行されます。
大きなオブジェクトの割り当てと割り当て解除を頻繁に行うと、LOH が断片化され、合計の空きメモリが必要以上に多くても、連続したメモリ領域がなくなる可能性があるため、OutOfMemory 例外が発生します。
現時点で考えられる回避策は 2 つあります。
- 64 ビット マシン/OS に移行して、それを活用してください:) (最も簡単ですが、リソースの制約によっては最も難しい場合もあります)
- #1 ができない場合は、断片化を避けるために、まず大量のメモリを割り当てて使用してください (小さな配列を操作するためのヘルパー クラスを作成する必要がある場合がありますが、実際にはそれはより大きな配列内に存在します)。これは少しは役立つかもしれませんが、問題を完全に解決できるわけではなく、複雑さに対処する必要があるかもしれません。
他のヒント
あなたはLOH(ラージオブジェクトヒープ)断片化の問題に遭遇してきたようです。
の屋根無しあなたがSOSを使用してLOHの断片化の問題を抱えているかどうかを確認することができます。
LOHを検査するためにSOSを使用する方法の例については、この質問を確認してください。
ガベージコレクションを強制すると(それが実際にいくつかの状況でのオブジェクトの寿命を促進することができる)常に良い考えではありません。あなたがする必要がある場合は、使用することになります:
array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];
このちょうど大きなオブジェクトヒープの断片化はありませんか?オブジェクトは> 85,000バイトは、大きなオブジェクトヒープに割り当てられています。 GCは、このヒープ内のスペースを解放しますが、残りのオブジェクトを圧縮することはありません。これは、正常ラージ・オブジェクトを割り当てるinsufficent連続したメモリをもたらすことができる。
アランます。
私はあなたの問題は、あなたがするVector3 [210210210]だけにするVector3 [200200200]から行っていることは本当にないと推測していた場合、あなたはこの1つ前に類似した前の手順を持っている可能性が高います:
i.e. // first you have Vector3[10,10,10]; // then Vector3[20,20,20]; // then maybe Vector3[30,30,30]; // .. and so on .. // ... // then Vector3[200,200,200]; // and eventually you try Vector3[210,210,210] // and you get an OutOfMemoryException..それが本当であれば、
、私はより良い配分戦略を示唆しています。配分の上に試してみてください - 多分対照的に、すべての時間が常に必要なだけのスペースを割り当てるためにサイズを倍増。これらのアレイは、これまでバッファを固定する必要があるオブジェクトが使用される場合は特に(すなわち、そのネイティブコードに関係している場合)
だから、代わりに上記の、このようなものを持っています:
// first start with an arbitrary size
Vector3[64,64,64];
// then double that
Vector3[128,128,128];
// and then.. so in thee steps you go to where otherwise
// it would have taken you 20..
Vector3[256,256,256];
彼らは集め取得されないことがあります。
テストとして、 WeakReferences 代わりに、それはあなたのOOMの問題を解決するかどうかを確認します。それはその後、しない場合は、どこか別の場所にそれらを参照しています。
私は(GCはその方法で微妙と怒りに迅速であるため)何をやろうとしているとすぐにガベージコレクションのためにプッシュしていることは、おそらく正しいアプローチではありません理解しています。
言った、あなたはを場合したいのそれを作成しない理由は、その機能、?
public static void Collect(ref object o)
{
o = null;
GC.Collect();
}
のOutOfMemory例外は内部で自動的に一度GCサイクルをトリガし、実際にあなたのコードに例外をスローする前に再度割り当てを試みます。あなたはあまりにも多くのメモリへの参照を保持している場合は、のOutOfMemory例外を持つことができる唯一の方法です。ヌルそれらを割り当てることによって、できるだけ早くすることができますように参照をクリアします。
問題の一部は、ラージ オブジェクト ヒープ上の単一の連続したメモリ ブロックとして表される多次元配列を割り当てていることである可能性があります (詳細は、 ここ)。これにより、たとえどこかにまだ空き領域があったとしても、使用できる連続した空きブロックがないため、他の割り当てがブロックされる可能性があり、したがって OOM が発生します。
ギザギザ配列 Vector3[210][210][210] として割り当ててみてください。これにより、配列が単一のブロックとしてではなくメモリ全体に分散され、問題が改善されるかどうかを確認してください。
ジョン、85000 バイトを超えるオブジェクトを作成すると、オブジェクトは大きなオブジェクト ヒープに格納されてしまいます。ラージ オブジェクト ヒープは決して圧縮されず、代わりに空き領域が再利用されます。これは、毎回より大きな配列を割り当てると、LOH が断片化され、OOM が発生する状況が発生する可能性があることを意味します。
これが事実であることを確認するには、OOM の時点でデバッガを中断してダンプを取得し、このダンプを接続バグを通じて MS に送信します (http://connect.microsoft.com) 素晴らしいスタートになるでしょう。
私が保証できるのは、GC は割り当て要求を満たすために正しい動作をするということです。これには、新しい割り当て要求を満たすために GC を開始して古いガベージをクリーンアップすることも含まれます。
Stackoverflow でメモリ ダンプを共有するポリシーがどのようなものであるかはわかりませんが、問題をさらに理解するために喜んで拝見させていただきます。