2005-04-13
| Table of Contents: |
| Rate This Article: | Add This Article To: |
( Page 3 of 5 )
Finalization
Finalization is a way for a resource to clean up after itself when that resource is being garbage collected. "Cleaning up" typically means closing open files, and cleaning up database connections and open TCP/IP sockets.
However, finalization does not restrict you in what can be done, so any cleanup functionality is fine.
To implement support for finalization in a new type, the new type has to override the default implementation of Finalize().
This example does just that:
public class MyFinalizableObject {
public MyFinalizableObject() {
}
protected override void Finalize() {
// TODO: perform resource cleanup here
// For now we only print the statement to the console
Console.WriteLine("Finalize() called");
}
}
After we define the new type, let's create an instance of that type:
MyFinalizableObject obj = new MyFinalizableObject();
Let's say that, after using that object for a while, we decide not to use it anymore. To explicitly let the runtime know we do not want to use that object anymore, assign null to the reference:
obj = null;
Sometime after that happens, the garbage collection engine will determine that this object is no longer reachable and has to be garbage collected. When trying to do garbage collection, the engine will find out that this object is finalizable (we'll go though the process in much more detail in a moment). Subsequently, the Finalize() method is called by the engine, and we'll see "Finalize() called" printed to the console. At that point, the garbage collector reclaims the memory previously allocated for that object and put it back into the managed heap.
Sounds simple enough, right? Let's look behind the scenes, to investigate the details of finalization and the costs associated with finalizable objects.
First, when you create a finalizable object, the object has to be placed on a RegisteredForFinalization queue. Placing the object on a queue is, in some sense, equivalent to having one extra pointer field in your object that is going to be initialized by the system. In addition, the current .NET implementation of the managed heap uses slower allocation for each finalizable object; you will notice the difference when allocating lots of small finalizable objects at a high rate.
When garbage collection engine starts the collection pass, it must do a weak pointer scan of the RegisteredForFinalization queue to find out if any objects in that queue are collectible. Any objects found to be collected are then moved to ReadyToFinalize queue.
After moving all collectible objects to ReadyToFinalize queue, all objects in the queue — and all objects reachable from objects in the queue — are marked as reachable. That isn't necessarily a good thing. All objects that would normally be garbage collected at this stage might potentially move to the next generation. Moving objects to the next generation in the generational garbage collector significantly increases the time-to-live for those objects. To make matters worse, the size of the graph of objects reachable from objects in the ReadyToFinalize queue is potentially huge. So we end up with a lot of no-longer-used objects hanging around for a long time, reducing the amount of memory available to the managed heap.
The .NET CLR currently uses a single high priority Finalizer thread to check on the ReadyToFinalize queue. This thread removes each object from the queue, executes its Finalize() method, and then proceeds to the next object. Using a single thread for finalization might affect the scalability of a multi-threaded application, especially in cases when lots and lots of objects are created and later must be garbage collected (and finalized). A single thread performing finalization might have a hard time keeping up with multiple threads rapidly creating new finalizable objects. At the same time, the Finalize() method can block the thread indefinitely; at that point, the managed process will start to leak resources and eventually die.
As we can see, finalization may come at considerable cost. The general rule of thumb is: if you can manage a proper cleanup of your object without making them finalizable, do it. Otherwise, keep reading further.
![]() |
|


