Understanding when to use a Finalizer in your .NET class
A common problem we see when moving to .NET all revolves around the finalizer. There are a few reasons that this happens:
- Developers move from C/C++ to C# and are used to created classes with a constructor and destructor.
- Developers don’t understand when they need to implement Dispose or Finalize and thus create them when they aren’t needed.
Finalization – what is it?
So what is Finalization and why is this such an important thing to understand? Well, there is a good description of it found here. Basically it is a way to make sure some code runs when an object gets cleaned up.
Why is it necessary?
If an object holds onto any unmanaged resources, such as file handles, sockets or database connections, the object is responsible for cleaning them up when it is destroyed. This only applies to unmanaged resources!
I would very strongly recommend that you read the documentation for the Finalize object very carefully before you use it. The things to watch for are:
- If you use an unmanaged object and hold a reference to it, you must implement a Finalizer to allow that unmanaged object to be cleaned up. Otherwise you will leak unmanaged (heap) memory.
- If you create a Finalize method (~Class in C#), even if it is empty, this will put the object on the finalize queue.
- If you have a Finalize method, don’t try to clean up managed objects from it. That is why most Finalize methods call Dispose(false). So they don’t clean up any managed objects. This is because finalized can happen in any order and just cause you hold a reference to an object, doesn’t mean it hasn’t been cleaned up already.
Why is a Finalize method bad?
So now we know how we should use the finalizer, but why is it so important to do it correctly? So items 1 and 3 above are pretty explanatory as to why you would want to do that. So what is wrong with #2?
If an object has a finalizer, it will be placed in the FinalizationQueue and is subject so some additional clean-up. Once the object is no longer referenced on a thread or from a global reference, the next time the Garbage Collector (GC) runs, it will see this object is ready to be collected. But it can’t collect it yet. It has to let the Finalizer run first. So the GC will finish collecting, then the Finalizer will Finalize the object, and then another GC collection will occur.
This can have a huge affect on performance as you should remember that all managed threads will be stopped waiting on the GC and then the GC will be stopped waiting on the Finalizer thread.
There is a lot more data out there about Finalization so I encourage you to read about it as much as possible so that you use it in the best way possible.