2012-02-21 15 views
0

このコードは、ある種のメモリリークを引き起こします。私はそれがnew byte[]によって引き起こされたと仮定します。 しかし、GCはこれを避けるべきではありませんか?プログラムは、十分な長さに実行した場合、コードはOutOfMemoryException例外OutOfMemoryException in C#

using (var file = new FileStream(fileLoc, FileMode.Open)) 
{ 
    int chunkSize = 1024 * 100; 
    while (file.Position < file.Length) 
    { 
     if (file.Length - file.Position < chunkSize) 
     { 
      chunkSize = (int)(file.Length - file.Position); 
     } 
     byte[] chunk = new byte[chunkSize]; 
     file.Read(chunk, 0, chunkSize); 
     context.Response.BinaryWrite(chunk); 
    } 
} 
+0

'context'とは何ですか? –

+0

HttpContextコンテキスト。 – chriszero

+1

プロファイラを使用して、このコードを原因と判断しましたか? –

答えて

5

問題は、あなたが新しい配列を繰り返し割り当てていることと、メモリ内で連続したブロックとして割り当てられていることがほぼ確実であるため、どのようにそれが噛み付いているのか理解できます。

バッファを一度しか作成せず、必要なチャンクサイズが標準のチャンクサイズより小さい場合は、バッファに一度だけ入るようにしてください。

using (var file = new FileStream(fileLoc, FileMode.Open)) { 
    int chunkSize = 1024 * 100; 
    byte[] chunk = new byte[chunkSize]; 

    while (file.Position < file.Length) { 
     if (file.Length - file.Position < chunkSize) { 
      chunkSize = (int)(file.Length - file.Position); 
      chunk = new byte[chunkSize]; 
     } 
     file.Read(chunk, 0, chunkSize); 
     context.Response.BinaryWrite(chunk); 
    } 
} 
+0

WhileループまたはForループ...良いキャッチNanhydrin私はあなたを今+1します – MethodMan

-1

の原因となります項目は、一般的に削除されますWHEN GCは規定しません。

+2

'IDisposable'メカニズムとガベージコレクタは完全に独立しています。 – Justin

+0

私は、アクティブなメモリ管理をそれほど有効な方法にしていないことを認識していますか? –

+1

割り振りシステムがどれほど怠けていても、メモリ不足になる前にガベージコレクタを実行する必要があります。 –

1

「十分に長い時間実行する」ということは、コードが少なくとも100 KB(ファイルが大きい場合はおそらくより大きい)の配列を割り当てているということを完全にはわかりません。それ自体ではおそらくエラーは発生しませんが、32 MB of virtual address spaceしかない環境では、これはかなり大きなメモリ割り当てです。これらの多くが並行して実行されている場合は、これを簡単に増やして比較的高いメモリ使用率にすることができます。この場合、OutOfMemoryExceptionが表示されることがあります。

using (var file = new FileStream(fileLoc, FileMode.Open)) 
{ 
    CopyStream(file, context.Response.OutputStream); 
} 
context.Responseあなたはちょうどあなたが以下のようなものを使用して、はるかに効率的にこれを行うことができ、その場合、HTTPレスポンスにファイルの内容を書き込むしようとしているように見える HttpResponseであると仮定して

CopyStreamの実装については、ファイル全体を一度に読み取ろうとするよりも小さなチャンクでデータをコピーするBest way to copy between two Stream instances - C#を参照してください。

+0

実際には100KBの配列です。 – ken2k

+0

@ ken2kありがとう、愚かな間違い - 修正。 – Justin

+0

Justinあなたのコードはどれくらいの大きさですか?実際にメモリリークが発生しているかどうかを明確にできるようにコードを投稿することが可能です。投稿された素晴らしい意見や意見がたくさんありますが、私はこれがオブジェクト、配列などのインスタンスを作成しているコード内の唯一の場所ではないと確信しています...ありがとう – MethodMan

2

バッファサイズを小さくすることをお勧めしますか?

問題は、85000バイトを超えるメモリブロックを繰り返し割り当てていて、残念ながら圧縮されていない特別なヒープ(ラージオブジェクトヒープ)に移動することが原因である可能性があります。

ラージオブジェクトヒープの仕組みの詳細については、hereを参照してください。 この残念ながら深刻なヒープの断片化につながり、最終的にあなたが記述しているようなメモリ不足エラーが発生する可能性があります(ここを参照してください:loh fragmentation causes OutOfMemory exception)を

あなたは(85,000バイトよりも小さい)小さなチャンクを割り当てる場合、それらが割り当てられます通常のヒープ上では、GCは圧縮を実行することができ、ほぼ確実に問題はなくなります。 @Nanhydrinが提案するようにコードを変更することを強く推奨します。これは、繰り返し割り当てを避け、やや良いパフォーマンスを発揮するからです。