17

いくつかの 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];

そのコードは(状況によっては)かなり頻繁に実行する必要がある可能性があるため、完全なガベージコレクションが良いアイデアかどうかはわかりません。

これを行う適切な方法はありますか?

4

10 に答える 10

15

これは、「GC を強制する方法」という元の質問に対する正確な回答ではありませんが、問題を再検討するのに役立つと思います。

皆さんのコメントを見てから、

  • GC.Collect(); を置く 問題を完全に解決するわけではありませんが、何らかの理由で、約1.3GBが割り当てられているときにプログラムがクラッシュします( System.GC.GetTotalMemory( false ); を使用して実際の割り当て量を見つけます)。

メモリの断片化が発生している可能性があります。オブジェクトが大きい場合 (私の記憶が正しければ .net 2.0 CLR で 85000 バイト、変更されたかどうかはわかりません)、オブジェクトは特別なヒープであるLarge Object Heap (LOH)に割り当てられます。GC は、LOH で到達不能なオブジェクトによって使用されているメモリを回収しますが、パフォーマンスのために、他のヒープ (gen0、gen1、および gen2) に対して行うように、LOH では圧縮を実行しません。

大きなオブジェクトの割り当てと割り当て解除を頻繁に行うと、LOH が断片化され、合計で必要以上の空きメモリがある場合でも、連続したメモリ空間がなくなる可能性があるため、OutOfMemory 例外が発生します。

現時点で考えられる回避策は 2 つあります。

  1. 64 ビット マシン/OS に移行して、それを活用してください :) (最も簡単ですが、リソースの制約によっては最も難しい可能性もあります)
  2. #1を実行できない場合は、最初に大量のメモリを割り当てて使用してください(実際には大きな配列に存在する小さな配列を操作するには、ヘルパークラスを作成する必要がある場合があります)。これは少しは役立つかもしれませんが、問題を完全に解決することはできず、複雑さに対処しなければならない場合があります。
于 2009-07-09T17:17:19.397 に答える
7

ガベージコレクションを強制することは必ずしも良い考えではありません(状況によっては、オブジェクトの存続期間を実際に促進する可能性があります)。必要な場合は、次を使用します。

array1 = null;
GC.Collect();
array1 = new Vector3[210,210,210];
于 2009-07-09T14:50:09.130 に答える
4

これは単なる大きなオブジェクト ヒープの断片化ではありませんか? 85,000 バイトを超えるオブジェクトは、大きなオブジェクト ヒープに割り当てられます。GC はこのヒープ内のスペースを解放しますが、残りのオブジェクトを圧縮することはありません。これにより、大きなオブジェクトを正常に割り当てるには、連続したメモリが不十分になる可能性があります。

アラン。

于 2009-07-09T17:21:08.437 に答える
3

私が推測しなければならなかったのは、Vector3[200,200,200] から Vector3[210,210,210] に移行しようとしているということではなく、この前に同様の手順を実行している可能性が高いということです。

すなわち   
    // 最初に持っている
    Vector3[10,10,10];
    // それから
    Vector3[20,20,20];
    // それなら多分
    Vector3[30,30,30];
    // .. 等々 ..
    // ...
    // それから
    Vector3[200,200,200];
    // そして最終的に試してみます
    Vector3[210,210,210] // OutOfMemoryException が発生します..

それが本当なら、私はより良い配分戦略を提案します。必要なスペースだけを常に割り当てるのではなく、毎回サイズを 2 倍にして、過剰に割り当ててみてください。特に、これらの配列が、バッファを固定する必要があるオブジェクトによって使用される場合 (つまり、ネイティブ コードに関連付けられている場合)

したがって、上記の代わりに、次のようなものがあります。

 // 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];
于 2009-07-09T14:59:38.097 に答える
1

予期しない場所で参照されているため、収集されていない可能性があります。

テストとして、代わりに参照をWeakReferencesに変更して、OOM の問題が解決するかどうかを確認してください。そうでない場合は、それらを別の場所で参照しています。

于 2009-07-09T14:59:16.837 に答える
0

OutOfMemory例外は、内部でGCサイクルを1回自動的にトリガーし、実際にコードに例外をスローする前に、割り当てを再試行します。OutOfMemory例外が発生する可能性がある唯一の方法は、大量のメモリへの参照を保持している場合です。nullを割り当てて、できるだけ早く参照をクリアします。

于 2009-07-09T17:03:20.500 に答える
0

ジョン、85000 バイトを超えるオブジェクトを作成すると、オブジェクトが大きなオブジェクト ヒープになってしまいます。大きなオブジェクト ヒープは決して圧縮されず、代わりに空き領域が再び再利用されます。これは、毎回より大きな配列を割り当てると、LOH が断片化され、OOM が発生する状況に陥る可能性があることを意味します。

OOM の時点でデバッガーを中断してダンプを取得することで、これが事実であることを確認できます。接続バグ ( http://connect.microsoft.com ) を介してこのダンプを MS に送信することは、すばらしい出発点です。

私が保証できるのは、GC が割り当て要求を満たすために正しいことを行うということです。これには、新しい割り当て要求を満たすために古いガベージをクリーンアップするために GC を開始することも含まれます。

Stackoverflow でメモリ ダンプを共有するポリシーがどのようなものかはわかりませんが、問題をより理解するために喜んで検討させていただきます。

于 2009-07-10T03:07:58.617 に答える
0

問題の一部は、多次元配列を割り当てていることである可能性があります。多次元配列は、大きなオブジェクト ヒープ上の 1 つの連続したメモリ ブロックとして表されます (詳細はこちら)。どこかに空き領域がまだある場合でも、使用する空き連続ブロックがないため、これにより他の割り当てがブロックされる可能性があるため、OOM が発生します。

ギザギザの配列 (Vector3[210][210][210]) として割り当ててみてください。これにより、配列が単一のブロックとしてではなく、メモリ全体に分散され、問題が改善されるかどうかを確認できます。

于 2009-07-09T17:23:05.530 に答える