web-dev-qa-db-ja.com

GC.SuppressFinalize()をいつ使用する必要がありますか?

.NETでは、どのような状況でGC.SuppressFinalize()を使用する必要がありますか?

この方法を使用するとどのような利点がありますか?

264
Sam Saffron

SuppressFinalizeは、ファイナライザーを持つクラスによってのみ呼び出される必要があります。 thisオブジェクトが完全にクリーンアップされたことをガベージコレクター(GC)に通知しています。

ファイナライザがある場合の推奨IDisposableパターンは次のとおりです。

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

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

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

通常、CLRは、オブジェクトの作成時にファイナライザーでオブジェクトのタブを保持します(作成コストが高くなります)。 SuppressFinalizeは、オブジェクトが適切にクリーンアップされ、ファイナライザーキューに移動する必要がないことをGCに伝えます。 C++デストラクタのように見えますが、動作はしません。

SuppressFinalizeの最適化は簡単ではありません。オブジェクトがファイナライザキューで長時間待機する可能性があるためです。気になる他のオブジェクトでSuppressFinalizeを呼び出そうとしないでください。それは起こるのを待っている重大な欠陥です。

設計ガイドラインでは、オブジェクトがIDisposableを実装している場合はファイナライザーは必要ないが、ファイナライザーがある場合は、クラスの確定的なクリーンアップを可能にするためにIDisposableを実装する必要があります。

ほとんどの場合、IDisposableを使用してリソースをクリーンアップできるはずです。オブジェクトが管理されていないリソースを保持している場合にのみファイナライザーが必要であり、それらのリソースがクリーンアップされることを保証する必要があります。

注:コードがIDisposableオブジェクトを適切に破棄したことをテストするために、コーダーが独自のIDisposableクラスのビルドをデバッグするファイナライザーを追加する場合があります。

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
    #if DEBUG
        GC.SuppressFinalize(this);
    #endif
    }

    #if DEBUG
    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
    #endif
280
Robert Paulson

ファイナライザーで行われる作業はすべて既に行われていることをシステムに伝えているため、ファイナライザーを呼び出す必要はありません。 .NETドキュメントから:

IDisposableインターフェイスを実装するオブジェクトは、IDisposable.Disposeメソッドからこのメソッドを呼び出して、ガベージコレクターがそれを必要としないオブジェクトでObject.Finalizeを呼び出さないようにすることができます。

一般に、ほとんどのDispose()メソッドはGC.SupressFinalize()を呼び出すことができます。これは、ファイナライザでクリーンアップされるすべてのものをクリーンアップする必要があるためです。

SupressFinalizeは、システムがオブジェクトをファイナライザスレッドに煩わせないようにする最適化を提供するものです。適切に記述されたDispose()/ finalizerは、GC.SupressFinalize()の呼び出しの有無にかかわらず適切に動作するはずです。

35
Michael Burr

このメソッドは、IDisposableを実装するオブジェクトのDisposeメソッドで呼び出す必要があります。この方法では、誰かがDisposeメソッドを呼び出した場合、GCはファイナライザーを再度呼び出しません。

参照: http://msdn.Microsoft.com/en-us/library/system.gc.suppressfinalize.aspx

2
albertein
 Dispose(true);
 GC.SuppressFinalize(this);

オブジェクトにファイナライザーがある場合、.netは参照をファイナライズキューに入れます

Dispose(ture)を呼び出しているため、オブジェクトがクリアされるため、このジョブを実行するためにファイナライズキューは必要ありません。

したがって、GC.SuppressFinalize(this)を呼び出して、ファイナライズキューの参照を削除します。

1
MaxJ

クラス、またはそれから派生したものがファイナライザを持つオブジェクトへの最後のライブ参照を保持する可能性がある場合、悪影響を受ける可能性のある操作の後に、オブジェクトでGC.SuppressFinalize(this)またはGC.KeepAlive(this)を呼び出す必要がありますそのファイナライザにより、その操作が完了するまでファイナライザが実行されないようにします。

GC.KeepAlive()GC.SuppressFinalize(this)のコストは、ファイナライザを持たないクラスでは基本的に同じであり、ファイナライザを持っているクラスは通常GC.SuppressFinalize(this)を呼び出す必要があるため、後者の関数をDispose()の最後のステップは必ずしも必要ではないかもしれませんが、間違っていません。

0
supercat