2009-02-22 14 views
93

CLRとGCの仕組みに魅了されています(これについては、C#、Jon Skeetの書籍/記事などでCLRを読んで知識を広げています)。とにかくオブジェクトをnullに設定するDispose()

、言っ違いです:

MyClass myclass = new MyClass(); 
myclass = null; 

または、MyClassのは、IDisposableインターとデストラクタを実装することと、()のDisposeを呼び出しては?

また、私はコードをステップ実行して使用ブロックを終了すると、そのときのオブジェクトまたはガベージコレクションが発生したときに、usingステートメント(たとえば以下)を持つコードブロックを持っていますか?私がブロックを使用しているときにDispose()を呼び出すと、何が起こりますか?

using (MyDisposableObj mydispobj = new MyDisposableObj()) 
{ 

} 

ストリームクラス(例:BinaryWriter)にはFinalizeメソッドがありますか?なぜ私はそれを使いたいのですか?

答えて

185

処理をガベージコレクションから切り離すことは重要です。それらは完全に別のもので、1つのポイントが共通しています。

Dispose、ガベージコレクションとファイナライズ

あなたがusing文を書くときDisposeusing文の本体のコードがスローた場合でも呼び出されるように、それはのtry/finallyブロックのために、単純にシンタックスシュガーです例外。それはではありませんは、オブジェクトがブロックの最後にガベージコレクションされていることを意味します。

廃棄は約アンマネージドリソース(非メモリリソース)です。これらは、UIハンドル、ネットワーク接続、ファイルハンドルなどである可能性があります。これらのリソースは限られているため、できるだけ早く解放したいと考えています。直接(通常IntPtrを介して)または間接的に(たとえばStreamSqlConnectionなどを介して)いずれかのタイプがアンマネージドリソースを「所有」している場合は必ずIDisposableを実装する必要があります。

ガーベジコレクション自体は、メモリについてのものです。ガベージコレクタは参照できなくなったオブジェクトを見つけ出し、解放することができます。しかし、ヒープが必要なことを検出したときだけ(例えば、ヒープの「世代」がメモリ不足の場合)、常にガベージが検索されることはありません。

ひねりはファイナライゼーションです。ガベージコレクタは、もはや到達可能ではなく、ファイナライザを持つオブジェクトのリストを保持しています(C#では~Foo()と書かれていますが、C++デストラクタのようなものではありません)。メモリが解放される前に余分なクリーンアップを行う必要がある場合に備えて、これらのオブジェクトのファイナライザを実行します。

ファイナライザは、ほとんどの場合、タイプのユーザが整然とした方法で処分するのを忘れた場合にリソースをクリーンアップするために使用されます。したがって、FileStreamを開いてもDisposeまたはCloseに電話するのを忘れた場合、ファイナライザはとなり、最終的にがあなたのためにファイルハンドルを解放します。よく書かれたプログラムでは、finalizersはほとんど私の意見では決して発射すべきではありません。

nullに変数を設定するにnull

一つの小さな点に変数を設定する - これは、ほとんどのガベージコレクションのために必要とされることはありません。私の経験では、オブジェクトの「一部」がもはや必要ではないことはまれですが、メンバ変数の場合にはそれを実行することがあります。ローカル変数の場合、JITは通常、レファレンスを再度使用しないときを知るのに十分な(リリースモードの)スマートです。たとえば、次のように

StringBuilder sb = new StringBuilder(); 
sb.Append("Foo"); 
string x = sb.ToString(); 

// The string and StringBuilder are already eligible 
// for garbage collection here! 
int y = 10; 
DoSomething(y); 

// These aren't helping at all! 
x = null; 
sb = null; 

// Assume that x and sb aren't used here 

それnullにローカル変数を設定すると、あなたがループにいるときであり、ループのいくつかの枝が変数を使用する必要がありますが、あなたを知っている価値があるかもしれない1時間あなたがしていないポイントに達しました。たとえば:

SomeObject foo = new SomeObject(); 

for (int i=0; i < 100000; i++) 
{ 
    if (i == 5) 
    { 
     foo.DoSomething(); 
     // We're not going to need it again, but the JIT 
     // wouldn't spot that 
     foo = null; 
    } 
    else 
    { 
     // Some other code 
    } 
} 

実装IDisposableを/ファイナライザ

だから、あなた自身のタイプは、ファイナライザを実装する必要がありますか?ほとんど間違いない。 間接的にの場合は、アンマネージドリソース(例:あなたはFileStreamをメンバ変数として持っています)あなた自身のファイナライザを追加することは助けになりません:あなたのオブジェクトがストリームの場合、ストリームはガベージコレクションの対象になりますので、ファイナライザを持つFileStreamに頼ることができますそれは他のものを参照するかもしれない、など)。管理されていないリソースを「ほぼ」直接保持したい場合は、SafeHandleがあなたの友人です。これに行くには少し時間がかかりますが、almostnever need to write a finalizer againとなります。できるだけ早くリソースのハンドル(IntPtr)を持っていれば、通常はファイナライザが必要です。できるだけ早くSafeHandleに移動してください。

ジョーダフィーは、読む価値があるvery long set of guidelines around finalizers and IDisposable(たくさんのスマートフォークと共同編集)を持っています。クラスを封印すると、より簡単になります。新しい仮想Dispose(bool)メソッドなどを呼び出すために、Disposeをオーバーライドするパターンは、クラスが継承のために設計されている場合にのみ関係します。

これは、散歩の少しでしたが、あなたには、いくつかのようにしたいところ明確化を依頼してください:)

+0

Re: "ローカル変数をnullに設定する価値があるかもしれません" - おそらく、より厄介な "キャプチャ"シナリオ(同じ変数の複数のキャプチャ) - しかし、ポストを複雑にする価値はないかもしれません! +1 ... –

+0

@Marc:それは本当です - 私はキャプチャされた変数についても考えていませんでした。うーん。ええ、私はそれを残すだろうと思う;) –

+9

うわー。私は最終的に、Skeetistsのカルトの源を理解します。この投稿は素晴らしいです! – JohnFx

19

オブジェクトを破棄すると、リソースは解放されます。変数にnullを代入すると、参照を変更するだけです。

myclass = null; 

あなたがこれを実行した後、オブジェクトMyClassのは、まだ存在して言及していました、そしてGCはそれをクリーンアップするに周り取得するまでに継続されます。 Disposeが明示的に呼び出された場合、または使用ブロックにある場合、すべてのリソースはできるだけ早く解放されます。

+7

それは* *まだその行を実行した後に存在しないかもしれない - それはゴ​​ミが収集された可能性があります*前*その行JITは、このようなスマートな作り方はほとんど常に無関係です。 –

+6

nullに設定すると、オブジェクトが保持するリソースが決して解放されないことを意味する可能性があります。 GCは処理を終了せず、オブジェクトが直接アンマネージリソースを保持し、そのファイナライザが処理しない(またはファイナライザがない)場合、それらのリソースはリークします。知っておくべきこと。 – LukeH

4

2つの操作はお互いにあまり関係がありません。 nullへの参照を設定すると、単にそのことが行われます。それは、まったく参照されたクラスに影響を与えません。 変数はもはや以前のオブジェクトを指していませんが、オブジェクト自体は変更されていません。

Dispose()を呼び出すと、オブジェクト自体のメソッド呼び出しです。 Disposeメソッドがどんな処理をしても、オブジェクトに対して実行されます。しかし、これはあなたのオブジェクトへの参照には影響しません。

重複の領域のみがは、オブジェクトへの参照がなくなったときに、それは最終的にガベージコレクションを取得するということです。また、クラスがIDisposableインターフェイスを実装している場合、ガベージコレクション前にオブジェクトに対してDispose()が呼び出されます。

しかし、あなたが参照をnullに設定した直後は、2つの理由でそれは起こりません。 最初に他の参照が存在する可能性があるため、ガベージコレクションはまだ行われません。また、最後の参照であってもガベージコレクションの準備が整っていても、ガベージコレクタが決定するまで何も起こりませんオブジェクトを削除します。

オブジェクトのDispose()を呼び出しても、オブジェクトは決して「強制終了」されません。これは一般的にオブジェクトを安全に削除できるようにクリーンアップに使用されますが、最終的にはDisposeに関して魔法のようなものはありません。それは単なるクラスメソッドです。

+0

私はこの答えを褒めたり、 "再帰的"という答えの詳細だと思います。 – Sung

関連する問題