05 November 2005

Determistic Finalization with IDisposable

Finalization, how it works?
Heap exhaustion/shutting down an application triggers a Garbage Collection. Class that manage external resources like DB connection, file handler should implement Finalization to improve the system performance – proactively release the (external) resource once finished use.

However, objects that require finalization complicate the collection process. An object with a finalizer is not immediately released. How is works? GC checks metadata of every object type in the scan. If the object implements the Finalize() method, GC doesn't destroy it. Instead it is marked as reachable and it moved from it's orginal graph to another object graph- a special queue called finalized queue, which establishes a reference to the object, preventing its collection. GC proceeds. Meanwhile, a seperate background thread iterates all objects in the finalized queue, calling Finalize() on each and removes the finalized object from the finalization queue.

There is a fair standard pattern on implementing Finalize, such like define it as proteced+virtual; calling parent's Finalize at the end of your call... In fact c# comes with a code template destructor (~{MyClassName}), compiler expands it to full size Finalize() method at compiling.

Only after finalization can the object be removed from memory during the *next* GC pass. As a side effect of surviving one collection pass, the object is promoted into a higher generation, further delaying its eventual collection-

Non-Determistic Finalization meaning Garbage collection time is unpreditable. The application should not rely on GC to clean up expensive resources, which hurts scalability and performance.

Determistic Finalization
To free up used resources, client should proactively release used resource, such like DB connection when it is no longer used, instead of relies on GC.

-The Open()/Close() Pattern
Use object pool to manage objects without really destroy an object. Many .Net framwork classes use this pattern, E.g. file, stream (I/O, memory, network), DB connection, communication port etc.

-IDisposable Pattern
Classes that require finalization should implement the IDisposable interface in order to allow client to provide a determistic finalisatoin - short-circuit GC finalization and avoid the garbaged object (which uses external resource, say) promoted to G1.
There are two ways that classes that implement IDisposable have their objects being cleaned up.

1) Client code explicitly calls Dispose.
Your implementation in Dispose has the _chance_ to explicitly clean-up unmanaged resource as well as managed resource (by GC). By now your unmanaged resource is already freed up, there is no point to put the object in finalize queue to delays its release (G1), you should supress this by calling GC.SuppressFinalize(this);

2) Destructor Finalize method calls Dispose. This is a fallback plan if client fails to clean up.
In this case the Dispose method can only clean up unmanaged resource. Because during finalization, GC may have already removed the object for which Dispose is called (in following code example, the timer object). In this case, Dispose calls to clean this object, it will fail on null referencing.

Sample code from MCSD training material:

// Implementing IDisposable implies that the
// instances of this class will use unmanaged resources

public class Parent:IDisposable
{
// An unmanaged resource
private IntPtr ptr;

// A Managed resource
private System.Timers.Timer timer;

// Variable to track call to Dispose method
private bool disposed;

public Parent()
{
// Implement constructor
}

public void Dispose()
{
// Call the overloaded Dispose method
// with true as argument, indicating that
// Dispose is called by the user of the object
Dispose(true);
// Suppress the Finalize method so that it does not call Dispose again
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool called_by_user )
{
if (!this.disposed)
{
// if the user of the object called the Dispose method
// Clean managed as well as unmanaged data
// Otherwise clean only unmanaged data
if (called_by_user)
{
// Clean managed data
timer.Dispose();
}
ptr = IntPtr.Zero;
disposed = true;
}
//base.Dispose();
}
// C# destructor which is used to execute the finalization code
~Parent()
{
Dispose(false);
}

}

public class Child:Parent
{.
.
.

protected override void Dispose(bool called_by_user )
{
// Cleanup code for the child object
.
.
.

// Call Dispose method of the Parent class
base.Dispose(called_by_user);

}
}