2009-08-22 9 views
1

は...のは、私は次のコードがあるとしましょうループスコープとメモリの問題

StringBuilder sb = new StringBuilder(); 
for (int i = 0; i < 10000; i++) 
{ 
    MyCustomClass myObj = new MyCustomClass(); 
    sb.Append(myObj.RenderShortString()); 
} 
Console.Write(sb.ToString()); 

そしてMyCustomClassが非常に大きいオブジェクトであることを前提としています。たとえば、1MBの文字列を含む内部メンバーを作成して保持しているとします。 RenderShortString()メソッドは、約100文字の文字列を単純にレンダリングします。

このループは10000回ループします。

私はループ内でSystem.OutOfMemory例外を引き起こしている基本的にこのようなものがあります。

私の質問は、myObjの各インスタンスに割り当てられたメモリスペースがガベージコレクタによってクリーンアップされたときに関係します。私はStringBuilderに問題があるとは思わないが、間違っているかもしれない。私は、myObjのインスタンスがメモリに割り当てられているが、ループが終了するまではクリーンアップに使用できないと感じる。これは正しいです?もしそうなら、私はレンダリングされた文字列を取得するとすぐに、そのインスタンスを完了したことをアプリケーションに伝えることができますか?

+0

ループ内の 'GC.Collect()'を明示的に呼び出すと問題が解決されますか?恒久的な解決策として素晴らしいアイデアではありませんが、以前のオブジェクトが収集可能であることが証明され、問題はGCが実行する機会を得られないという「ちょうど」です。 – stevemegson

答えて

2

あなたが実際にそれが同様にかかり、非常にメモリとすることができるためのStringBuilderが、どのように動作しているかで二ご覧になる場合があります。

MyCustomClassはメモリが重いと言っても問題はありませんが、エッジを越えてプロセスをプッシュするのに貢献している可能性があります。

StringBuilderは空き領域がなくなるたびに、元のバッファの2倍の新しいバッファを再割り当てし、古い​​文字をコピーし、古いバッファにGC'dを取得させます。あなたが割り当てることが許可されているメモリよりも2倍大きいように、あなたはちょうど十分なもの(xと呼んでいる)を使用している可能性があります。あなたは、あなたの文字列の最大長を決定し、それをStringBuilderのコンストラクタに渡して、事前に割り当てることができます。倍増した再配置の慈悲ではありません。

See this topic

+0

良い点。これは、StringBuilderの現実的な初期容量を選択することで軽減できます。 1回の呼び出しで100文字の文字列が作成されると、100万の容量が必要になることがわかります。それでも、この効果は問題になることはまずありません。多くの場合、メモリの2倍の容量が使用されます(2MBバッファ+ 1MB + 500 KBなど)。 – Thorarin

+2

有効なポイントですが、OPがこれを明確に示す数値は根本的な問題ではありません。 –

+0

すべての回答が役に立ちました。しかし、私は実際の例では文字列作成者を詳しく見ていきます。私は後の答えで示唆されているようにプロファイラを使用するつもりです。ありがとう! – Kevin

4

簡単な答え:わからないことがあります。 .NETガベージコレクションは決定的ではありません。 System.GC.Collectメソッドでガベージコレクションを強制することができます。それ以外に、GCは、到達不能オブジェクトに割り当てられたメモリが最終的に解放されることを保証します。

+0

しかし、メモリが不足している場合は、ガベージコレクタが「余分な実行」をして、破棄の準備ができているかどうかを確認しませんか? –

+0

メモリ不足の状態で動作します。しかし、十分なメモリを解放できない場合、ランタイムは 'OutOfMemoryException'をスローします。 –

5

.netでガベージコレクションの「機能」が表示されています。オブジェクトはスコープの外に一旦破棄され、各繰り返しでスコープから外れていますが、GCが非決定論的なのでいつからか分かりません。

は、ここでは、このビットは説明しています:Loops and Garbage Collection

をまた、ここでは.NETのGCで行われ、興味深い研究があります。可能であれば、ループ内で "new"を使用することを避けることを提案しています。

http://nerds-central.blogspot.com/2008/10/net-garbage-collector-pain.html

2

RenderShortString()が完了したときに、各MyCustomClassインスタンスは、コレクションの対象となります - いくつかのケースでは、中にそのメソッドの実行を、さらに

実際のガベージコレクションは、ガベージコレクタがそのように感じるときにのみ発生しますが、恐らくはMyCustomClassインスタンスがすぐにgen0から収集されます。

オブジェクト自体は、大きな文字列を参照するため、ではなく、であることに注意してください。これらの大きな文字列は、large object heapに割り当てられますが、まだガベージコレクトされていますが、圧縮されていません。あなたのアプリケーションがかなりの量のメモリを占有しているのを見ているなら、MyCustomClassのインスタンスはガベージコレクトされているが、その文字列はガベージコレクションされていない可能性があります。

実際に.NETで非常に大きなカスタムオブジェクトを作成するのはかなり難しいです。明白な例は以下のとおりです。

  • 長い文字列の要素の多くが付いて
  • 配列構造体自体が大きな固定のバッファが含まれている
  • 箱入り構造体は、ほとんどの部分について

(これはまれでなければなりません) 、オブジェクト自体はかなり小さいですが、多くのオブジェクトがあります。

1

どのオブジェクトがどのくらいのメモリを割り当てているかを確認するには、プロファイラを実行する必要があります。 Visual Studioのプロファイラでは、各世代のオブジェクト数と、オブジェクト数を指定できます。