13

ループ内で大きな文字列を継続的に構築し、それらをデータベースに保存する必要がありますが、現在、OutOfMemoryException.

ここで基本的に行われているのは、いくつかのデータに基づいてXmlWriterwithを使用して文字列を作成することです。StringBuilder次に、この xml 文字列を別の文字列に変換する外部ライブラリのメソッドを呼び出します。その後、変換された文字列がデータベースに保存されます。このすべてが、さまざまなデータに対して約 100 回のループで繰り返し実行されます。

文字列自体はそれほど大きくなく (それぞれ 500kByte 未満)、プロセス メモリはこのループ中に増加しません。それでも、時々私はOutOfMemeoryExcpetion以内にStringBuilder.Append. 興味深いことに、この例外によってクラッシュが発生することはありません。その例外をキャッチして、ループを続行できます。

ここで何が起こっているのですか?OutOfMemoryExceptionシステムに使用可能な空きメモリがまだ十分にあるのに、なぜエラーが発生するのですか? これはGCヒープの問題ですか?

これらすべての文字列の変換を回避できない場合、これを確実に機能させるにはどうすればよいでしょうか? GC コレクションを強制する必要がありますか? Thread.Sleepaをループに入れる必要がありますか? 使用をやめるべきStringBuilderですか?OutOfMemoryException?に直面したときは、単純に再試行する必要があります。

4

3 に答える 3

16

メモリはありますが、文字列ビルダーのサイズを処理できる連続したセグメントがありません。文字列ビルダーのバッファーが短すぎるたびに、そのサイズが 2 倍になることを知っておく必要があります。ビルダーのサイズを (ctor で) 定義できる場合は、そのほうがよいでしょう。GC.Collect()オブジェクトの大規模なコレクションを処理し終わったら、呼び出すことができます。

実際には、OutOfMemory がある場合、一般的に悪い設計が示されます。メモリの代わりにハード ドライブ (一時ファイル) を使用する可能性があります。メモリを何度も割り当てるべきではありません (オブジェクト/バッファ/... を再利用してみてください)。 .

Eric Lippertの記事「Out Of Memory」は物理メモリを参照していません。この記事を読むことを強くお勧めします。

于 2009-11-20T09:57:45.490 に答える
3

あなたが言及したサイズでは、おそらくラージ オブジェクト ヒープ(LOH) の断片化が発生しています。

StringBuilder オブジェクトを再利用することは直接的な解決策ではありません。基礎となるバッファーを把握する必要があります。
可能であれば、事前にサイズを計算または見積もり、事前に割り当てます。

また、割り当てを切り上げると、たとえば 20k の倍数にすると役立ちます。これにより、再利用が改善される可能性があります。

于 2009-11-20T10:49:07.317 に答える
3

データ生成を行うときは、StringBuilder オブジェクトを再利用してみてください。

使用後または使用前に、StringBuilder のサイズを 0 にリセットして追加を開始します。これにより、割り当ての数が減少し、OutOfMemory の状況が非常にまれになる可能性があります。

私の要点を説明するには:

void MainProgram()
{
    StringBuilder builder = new StringBuilder(2 * 1024); //2 Kb

    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
}

void PerformOperation(StringBuilder builder)
{
    builder.Length = 0;

    //
    // do the work here builder.Append(...);
    //
}
于 2009-11-20T10:12:38.520 に答える