2009-09-10 10 views
6

先日、thisの記事を読んでいて、なぜDisposeメソッドとともにFinalizerがあるのだろうと思っていました。私はhereを読んで、あなたがなぜFinalizerにDisposeを追加したいのかを読んでいます。私の好奇心は、FinalizerがいつDisposeメソッドそのものよりも呼び出されるのでしょうか?コード例がありますか、またはソフトウェアが実行しているシステムで起こっていることに基づいていますか?その場合、GCがDisposeメソッドを実行しないようにすることができます。メソッドはいつ呼び出されないのでしょうか?

答えて

9

ファイナライザの目的は、単にメモリリークに対する安全対策です(Disposeを明示的に呼び出すではない場合)。また、GCがすべてのオブジェクトを最終的にファイナライズして収集するため、プログラムのシャットダウン時にリソースを解放したい場合は、オブジェクトを破棄する必要はありません。

ファイナライザからオブジェクトを処理するときに、オブジェクトを少し違って配置することが重要です。

~MyClass() 
{ 
    Dispose(false); 
} 

public void Dispose() 
{ 
    Dispose(true); 
    GC.SuppressFinalize(this); 
} 

protected void Dispose(disposing) 
{ 
    if (!this.disposed) 
    { 
     if (disposing) 
     { 
      // Dispose managed resources here. 
     } 
     // Dispose unmanaged resources here. 
    } 
    this.disposed = true; 
} 

あなたfinaliserで管理リソースを配置したくないんの理由は、あなたが実際にそうすることでそれらへの強い参照を作成することになり、これは、それが適切に仕事ですやって収集からGCを防ぐことができるということですそれら。 CLRにはそれらの知識がないため、アンマネージリソース(Win32ハンドルなど)はもちろん、明示的にクローズ/廃棄する必要があります。

+1

ファイナライザに管理対象リソースを廃棄しない別の理由...ファイナライザが実行されるまでにすでにGCされている可能性があります。既に収集されたときにそれらを破棄しようとすると、ランタイムエラーが発生します。 – LukeH

+1

@ルーク:本当ですが、すべての参照をnullに設定してから廃棄する前にヌルチェックを行うだけで、簡単に回避できます。 – Noldorin

+0

@ Noldorin - あなたの事例にイベントの登録を解除するのはどこですか?私はtechincincally彼らが管理下に落ちるかもしれないことを理解しますが、私たちはイベントを通してこのクラスに結びつけられたオブジェクトを持っていて、非管理部分でそれを登録解除してはいけないでしょうか?(ユーザがGCをきれいにするためにアップ)。それは起こることを確かめるために、管理されたセクションにイベントを登録解除することは安全でしょうか?副作用は誰かが彼らがオブジェクトだと思っているかもしれませんが、実際にはこのクラスと他のクラスとの間のイベントリンクのために決して廃棄されません。 – SwDevMan81

4

これは主にあなた自身を守るためのものです。あなたのクラスのエンドユーザーが何をするのかを指示することはできません。 Disposeメソッドに加えてファイナライザを提供することで、GCはDispose()メソッドを呼び出したり、クラスを誤って使用したりしても、オブジェクトを適切に解放します。

+1

GCが非決定的であることに言及する価値はあるので、ファイナライザがいつ呼び出されるか、*、場合でも*の保証はありません。 – LukeH

+0

はい - プログラムの実行時間が長くなると、オブジェクトが最終的に完成する可能性が高くなります。また、正常にシャットダウンすると、ファイナライズされます。しかし、GCには保証がありません。これは、IDisposableが最初に存在する理由の一部です。 –

1

disposeメソッドは、Dispose()を呼び出すか、usingステートメントでオブジェクトを指定することによって明示的に呼び出される必要があります。 GCは常にファイナライザを呼び出すので、オブジェクトがファイナライザを処理する前に何か起こる必要がある場合は、少なくともオブジェクト内のすべてがクリーンアップされていることを確認する必要があります。

ファイナライザのオブジェクトを可能な限りクリーンアップしないようにしたいのは、(disposeを呼び出すなどの)手作業で処理するよりも余計な作業が必要だからです。オブジェクトがある場合は常にファイナライザをチェックインしてくださいそれを取り除く必要があります。

2

オブジェクトがガベージコレクションされるときにファイナライザが呼び出されます。 Disposeを明示的に呼び出す必要があります。次のコードではファイナライザが呼び出されますが、Disposeメソッドは呼び出されません。

class Foo : IDisposable 
{ 
    public void Dispose() 
    { 
    Console.WriteLine("Disposed"); 
    } 

    ~Foo() 
    { 
    Console.WriteLine("Finalized"); 
    } 
} 

... 

public void Go() 
{ 
    Foo foo = new Foo(); 
} 
+1

それは完全に真実ではありません。ファイナライザは、オブジェクトがガベージコレクションに適格となった後(つまり、アプリケーションがもはやインスタンスを参照しない)、しばらく呼ばれます。ただし、ファイナライザをインスタンスに対して実行する必要があるため、CLRは実際にオブジェクトをルーティングするため、ファイナライザが実行されるまでガベージコレクションは行われません。 –

+0

オブジェクトが* GC *されるか、ファイナライザが呼び出されるという保証もありません。そのため、 'IDisposable'オブジェクトを正しく処分することが二重に重要です。 – LukeH

0

まだ言及されていない重要な微妙なノート:廃棄のめったに考慮されなかっ目的は、途中でクリーンアップされてからオブジェクトを防ぐためです。ファイナライザを持つオブジェクトは、最終的にを実行する前に、予想よりもを慎重に書き込む必要があります。ファイナライザは、オブジェクト(*)に対して行われる最後のメソッド呼び出しの開始前には実行できませんが、中にが実行されることがあります。メソッドが完了するとオブジェクトが破棄される場合があります。適切にDisposeを行うコードは、Disposeを呼び出す前にオブジェクトを放棄することはできません。したがって、Disposeを適切に使用するコードにファイナライザが混乱する危険はありません。一方、オブジェクトを使用する最後のメソッドが、オブジェクト参照自体の最後の使用後にファイナライザでクリーンアップされるエンティティを使用する場合、ガベージコレクタはオブジェクトに対してFinalizeを呼び出してクリーンアップすることができますまだ使用されているエンティティを削除します。解決策は、ファイナライザによってクリーンアップされるエンティティを使用するコールメソッドが、ある時点で "this"を使用するメソッド呼び出しによって追跡されることを保証することです。 GC.KeepAlive(これ)は、これを使用するのに適した方法です。

(*)オブジェクトに何もしないインラインコードに展開される非仮想メソッドは、このルールから除外される場合がありますが、Disposeは通常、仮想メソッドであるか、呼び出します。

関連する問題