Saturday, July 26, 2008

Dispose & Finalize

Finalization is the process by which the GC allows objects to clean up any unmanaged resources that they're holding, before the actually destroying the instance. An implementation of the Finalize method is called a "finalizer." Finalizers should free only external resources held directly by the object itself. The GC attempts to call finalizers on objects when it finds that the object is no longer in use—when no other object is holding a valid reference to it. In other words, finalizers are methods that the GC calls on "seemingly dead objects" before it reclaims memory for that object.

The GC calls an object's finalizer automatically, typically once per instance—although that's not always the case. You should never rely on finalizers to clean up managed resources. A class that has no finalizer implemented but is holding references to unmanaged objects can cause memory leaks, because the resources might become orphaned if a class instance is destroyed before releasing the unmanaged objects.

The time and order of execution of finalizers cannot be predicted or pre-determined. This is why you'll hear that the nature of finalization is "non-deterministic." Further, due to the non-deterministic nature of finalization the framework does not and cannot guarantee that the Finalize method will ever be called on an instance. Hence, you cannot rely upon this method to free up any un-managed resources (such as a file handle or a database connection instance) that would otherwise not be garbage collected by the GC.

Note that you cannot call or override the Finalize method. It is generated implicitly if you have a destructor for the class.

class Test
{

// Some Code

~Test
{
//Necessary cleanup code
}
}

In the preceding code, the ~Test syntax declares an explicit destructor in C#, letting you write explicit cleanup code that will run during the finalize operation.

The framework implicitly translates the explicit destructor to create a call to Finalize:

   protected override void Finalize()
{
try
{
//Necessary cleanup code
}
finally
{
base.Finalize();
}
}


Note that the generated code above calls the base.Finalize method.

You should note the following points should when implementing finalizers:

  • Finalizers should always be protected, not public or private so that the method cannot be called from the application's code directly and at the same time, it can make a call to the base.Finalize method
  • Finalizers should release unmanaged resources only.
  • The framework does not guarantee that a finalizer will execute at all on any given instance.
  • Never allocate memory in finalizers or call virtual methods from finalizers.
  • Avoid synchronization and raising unhandled exceptions in the finalizers.
  • The execution order of finalizers is non-deterministic—in other words, you can't rely on another object still being available within your finalizer.
  • Do not define finalizers on value types.
  • Don't create empty destructors. In other words, you should never explicitly define a destructor unless your class needs to clean up unmanaged resources—and if you do define one, it should do some work. If, later, you no longer need to clean up unmanaged resources in the destructor, remove it altogether.
Finalize() is a non-explicit way to clean up resources. Because you can't control when (or even if) the GC calls Finalize, you should treat destructors only as a fallback mechanism for releasing unmanaged resources. Instead, the approved way to release unmanaged resources is to make your class inherit from the IDisposable interface and implement the Dispose() method


No comments: