web-dev-qa-db-ja.com

.NETのMemoryStreamが閉じられていない場合、メモリリークが発生しますか?

私は次のコードを持っています:

MemoryStream foo(){
    MemoryStream ms = new MemoryStream();
    // write stuff to ms
    return ms;
}

void bar(){
    MemoryStream ms2 = foo();
    // do stuff with ms2
    return;
}

私が割り当てたMemoryStreamが後で何らかの方法で破棄されない可能性はありますか?

これを手動で閉じると主張するピアレビューがありますが、彼が有効なポイントを持っているかどうかを知るための情報を見つけることができません。

106
Coderer

何かが使い捨ての場合は、常に廃棄する必要があります。 bar()メソッドでusingステートメントを使用して、ms2は破棄されます。

最終的にはガベージコレクターによってクリーンアップされますが、Disposeを呼び出すことは常に良い習慣です。コードでFxCopを実行すると、警告としてフラグが立てられます。

58
Rob Prouse

少なくとも現在の実装では、何もリークしません。

Disposeを呼び出しても、MemoryStreamが使用するメモリはより速くクリーンアップされません。それwill呼び出し後の読み取り/書き込み呼び出しに対してストリームが実行可能になるのを停止します。

neverがMemoryStreamから別の種類のストリームに移動することを絶対に確信している場合、Disposeを呼び出さないことは害になりません。ただし、doを変更して別のストリームを使用する場合は、簡単な方法を早期に選択したため、見つけにくいバグに噛まれたくないため、一般的には良い習慣です。に。 (一方で、YAGNIの議論があります...)

とにかくそれを行うもう1つの理由は、新しい実装mayがDisposeで解放されるリソースを導入することです。

159
Jon Skeet

はい、aリーク、LEAKの定義方法と後の意味に応じて...

リークによって「メモリは割り当てられたままで、使用し終わっても使用できない」ことを意味し、後者によってdisposeを呼び出した後はいつでも意味する場合は、永続的ではないがアプリケーションランタイムの寿命)。

MemoryStreamで使用されているマネージメモリを解放するには、参照を解除する必要があります、参照を無効にして、すぐにガベージコレクションの対象となります。これを行わないと、使用中にメモリが割り当てに使用できなくなるため、使用が終了してから参照がスコープから外れるまで一時的なリークが発生します。

(単純にdisposeを呼び出すよりも)usingステートメントの利点は、usingステートメントで参照を宣言できることです。 usingステートメントが終了すると、disposeが呼び出されるだけでなく、参照がスコープ外になり、参照を事実上無効にして、「reference = null」コードを記述することを忘れずにオブジェクトをガベージコレクションに適格にします。

すぐに何かを参照解除しないと、古典的な「永続的な」メモリリークではありませんが、間違いなく同じ効果があります。たとえば、(disposeを呼び出した後でも)MemoryStreamへの参照を保持し、メソッドの少し下にさらにメモリを割り当てようとすると、まだ参照されているメモリストリームで使用されているメモリが使用できなくなります。破棄を呼び出して使用し終わったとしても、参照を無効にするか範囲外になるまで、.

25
Triynko

これはすでに答えられていますが、情報隠蔽の古き良き原則により、将来的にはリファクタリングが必要になる可能性があることを付け加えます。

MemoryStream foo()
{    
    MemoryStream ms = new MemoryStream();    
    // write stuff to ms    
    return ms;
}

に:

Stream foo()
{    
   ...
}

これは、呼び出し元が返されるStreamの種類を気にするべきではないことを強調し、内部実装を変更することを可能にします(たとえば、単体テストのためにモックするとき)。

バーの実装でDisposeを使用していない場合、問題が発生する必要があります。

void bar()
{    
    using (Stream s = foo())
    {
        // do stuff with s
        return;
    }
}
8
Joe

すべてのストリームはIDisposableを実装します。 usingステートメントでメモリストリームをラップすると、元気になります。 usingブロックは、ストリームが何であれ閉じられ、破棄されることを保証します。

fooを呼び出す場所ならどこでも(MemoryStream ms = foo())を使用して行うことができ、それでも大丈夫だと思います。

5
Nick

.Dispose()(またはUsingでラップ)を呼び出す必要はありません。

.Dispose()を呼び出す理由は、できるだけ早くリソースを解放するです。

たとえば、スタックオーバーフローサーバーについて考えてみましょう。このサーバーでは、メモリのセットが限られており、数千のリクエストが着信します。新しい着信要求の場合。

5
Jeff Atwood

主に一貫性を保つために、usingステートメントのbar()でMemoryStreamをラップすることをお勧めします。

  • 現時点では、MemoryStreamは.Dispose()のメモリを解放しませんが、将来のある時点で、またはあなた(またはあなたの会社の誰か)が独自のカスタムMemoryStreamで置き換える可能性がありますなど.
  • all Streamsが破棄されるようにプロジェクトにパターンを確立すると、「一部のStreamsを破棄する必要がありますが、特定のStreamsする必要はありません」...
  • 他のタイプのストリームを返すことができるようにコードを変更した場合、とにかく破棄するように変更する必要があります。

IDisposableを作成して返すときにfoo()のような場合に私が通常行うもう1つのことは、オブジェクトの構築とreturnの間の障害が例外によってキャッチされ、オブジェクトが破棄されるようにすることです。例外を再スローします。

MemoryStream x = new MemoryStream();
try
{
    // ... other code goes here ...
    return x;
}
catch
{
    // "other code" failed, dispose the stream before throwing out the Exception
    x.Dispose();
    throw;
}
2

メモリをリークすることはありませんが、コードレビューアーは、ストリームを閉じる必要があることを示しています。そうするのは礼儀正しいことです。

メモリをリークする可能性がある唯一の状況は、誤ってストリームへの参照を残し、決して閉じない場合です。あなたはまだ実際にメモリをリークしていませんが、あなたはそれを使用していると主張する時間を不必要に延長しますare.

2
OwenP

オブジェクトがIDisposableを実装する場合、完了したら.Disposeメソッドを呼び出す必要があります。

一部のオブジェクトでは、DisposeはCloseと同じことを意味し、その逆も同様です。その場合、どちらかが適切です。

さて、あなたの特定の質問に対して、いいえ、あなたはメモリをリークしません。