What's the purpose of GC.SuppressFinalize(this

2019-01-13 01:11发布

问题:

I have the following code:

public void Dispose()
{
    if (_instance != null)
    {
        _instance = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
    }
}

Although there is a comment that explains purpose of that GC-related call, I still don't understand why it's there.

Isn't object destined for garbage collection once all instances cease from existence, like, when used in using block?

What's the use case scenario where this would play important role?

回答1:

When implementing the dispose pattern you might also add a finalizer to your class that calls Dispose(). This is to make sure that Dispose() always gets called, even if a client forgets to call it.

To prevent the dispose method from running twice (in case the object already has been disposed) you add GC.SuppressFinalize(this);. The documentation provides a sample:

class MyResource : IDisposable
{
    [...]

    // This destructor will run only if the Dispose method 
    // does not get called.
    ~MyResource()      
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue 
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed 
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up 
            // unmanaged resources here.
            resource.Cleanup()          
        }
        disposed = true;         
    }
}


回答2:

Garbage collection: GC reclaims the memory used by the object when the object is referenced no more.

Dispose: a method from the IDisposable interface that is to release all managed and unmanaged resources when the programmer calls it (either directly or indirectly via a using block).

Finalizer: a method to release all unmanaged resources. Called by the GC before reclaiming the memory.

Managed resource: any .NET class that implements the IDisposable interface, like Streams and DbConnections.

Unmanaged resource: the stuffing wrapped in the Managed Resource classes. Windows handles are the most trivial examples.


Now to answer your question:

GC keeps a list (Finalization Queue) of all objects the class of which declares a Finalizer (~ClassName in C#). Objects are put in this queue upon creation. GC runs periodically to check whether there are any objects inaccessible from the program. It then checks if any of the inaccessible objects are referenced from the Finalization Queue, and puts these in another queue called the Freacheable queue, while the rest are reclaimed. A separate thread is used to run the Finalize methods of objects in the Freacheable queue.

The next time GC runs, it will find that some of the objects previously in the Freacheable queue are already Finalized thus are ready for reclaiming. Note that GC needs at least two cycles (or a lot more if there is a lot of Finalization to do) to get rid of an object with a Finalizer, which incurs some performance penalties.

The SuppressFinalize method simply sets a flag in the object header which indicates that the Finalizer does not have to be run. This way GC can reclaim the memory of the object right away. As per the definition above, the Dispose method does the same thing as the Finalizer (and more), so if it is executed then Finalization is not neccessary any more. Using the SuppressFinalize method, you can save some work for the GC by notifying it about this fact. Additionally, now you don't have to implement checks in the Finalizer to avoid double releasing. The only problem with Dispose is that is is not guaranteed to run, because it is the programmer's responsibility to call it, that is why sometimes we need to bother with Finalizers.


That being said, it is only very very rarely that you need to write a Finalizer, because for the vast majority of the usual unmanaged resources a managed wrapper already exists, and managed resources are to be released by calling their Dispose methods from your own Dispose method, and from there only! In finalizers you must never call a Dispose method.


Further reading:

  • Dispose, Finalization, and Resource Management
  • http://www.codeproject.com/KB/dotnet/idisposable.aspx


回答3:

Objects which can be finalized survive the first GC run.

Normally, when the GC detects that an object is unreachable, then it reclaims it. If the object is finalizable, then the GC does not reclaim it; instead, it considers it reachable nonetheless (and all the objects that this object references, and so on), and schedules it for finalization. The object will be reclaimed only when it is find again to be unreachable at some point after it has been finalized.

This means that a finalizable object incurs an extra cost: the object must be kept around in memory for a longer time. Hence the call you see: it is worthwhile to suppress finalization when it is not needed. Here, the object uses finalization to ensure that it is always "disposed of" at some point. When it is disposed explicitly, it needs not be finalized anymore.



回答4:

If your type implements a finalizer (~MyType() { }), it keeps the garbage collector from running it. Used when your finalizer takes care of unmanaged types, but the user has already called Dispose() (either explicitly or via a using() { } block), freeing those unmanaged types.



回答5:

From MSDN: GC.SuppressFinalize:

This method sets a bit in the object header, which the system checks when calling finalizers. The obj parameter is required to be the caller of this method.

Objects that implement the IDisposable interface can call this method from the IDisposable..::.Dispose method to prevent the garbage collector from calling Object..::.Finalize on an object that does not require it.

Typically you would use this if your object does not reference other objects, just discrete types, or has already reset any object references to NULL.