I have talked about general memory management in .NET a whole bunch on this blog but I have been kicking around some thoughts about the way Finalizer Queue works .NET and how we might avoid shooting ourselves in the foot when using Finalizers.
Finalizers (I would have called this a destructor in my C++ days) are used to perform any important clean-up when a class instance is being cleaned up by the garbage collector (GC’ed).
Here is what it would look like:
class Animal {
~Animal()
{
}
}
In reality very few developers use this pattern, in fact it is recommended you use the IDisposable interface, which requires the implementation of a single parameterless method called Dispose:
public void Dispose() {
// Dispose of unmanaged resources
Dispose(true);
// Suppress finalization.
GC.SuppressFinalize(this); }
The dispose pattern requires two Dispose methods to be implemented and doesn't require overriding the Object.Finalize method. The publicly accessible Dispose method is called by the object consumer, and its purpose is to free unmanaged resources and just as importantly to indicate that the finalizer doesn't have to run.
Finalizer (+ Freachable) Queue
If an object type does override the Finalize method, the garbage collector immediately adds an entry for each instance to an internal structure called the finalization queue. This queue contains entries for all the objects in the managed heap with a defined finalizer, and this finalization code must run before the garbage collector can begin reclaiming the instance memory.
GC doesn’t call the Finalize method directly from this queue, instead it removes object reference from the finalizer queue and puts it on the freachable queue. Each object reference in the freachable queue identifies an object that is ready to have its Finalize method called.
Finalization Thread
The Finalizer thread is a dedicated thread created by the CLR in every .NET process. It is responsible for running the finalize method for all objects that opted to implement it. The finalization queue contains entries for all the objects in the managed heap whose finalization code must run before the garbage collector can reclaim their memory. This specialized CLR thread is only responsible for monitoring the freachable queue and when GC adds an item it executes the Finalize method.
Potential issues with Finalization Queues
Just a couple of concepts ideas and thoughts on finalization for your consideration:
- Only use Finalizers if you really need them, you will waste resources unnecessarily
- Empty finalizers are ultimately wasteful, and should be removed, instances get into the finalize queue for no reason.
- The GC will attempt to run each finalizer to completion.
- If finalizer has slow or blocking code it will impact all queued finalizers probably resulting in memory growth and more GC attempts (high CPU).
- Finalizers cannot be called directly, they are invoked by the finalization thread (but take a look at Dispose pattern).
- The Finalize method is called recursively for all instances in the inheritance chain, from the most-derived to the least-derived.
- An object requiring finalization dies, lives, and then dies again.
Comments are closed.