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?
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 calledDispose()
(either explicitly or via ausing() { }
block), freeing those unmanaged types.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, theDispose
method does the same thing as the Finalizer (and more), so if it is executed then Finalization is not neccessary any more. Using theSuppressFinalize
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 withDispose
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 ownDispose
method, and from there only! In finalizers you must never call a Dispose method.Further reading:
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.
From MSDN: GC.SuppressFinalize:
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.
When implementing the dispose pattern you might also add a finalizer to your class that calls
Dispose()
. This is to make sure thatDispose()
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: