Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 2

You must implement finalizers very carefully; it's a complex operation that can carry considerable

performance overhead. The performance overhead stems from the fact that finalizable objects
are enlisted and removed from the finalization queues, which are internal data structures
containing pointers to instances of classes that implement a finalizer method. When pointers to
these objects are placed in this data structure, the object is said to be enlisted in the Finalization
Queue. Note that the GC periodically scans this data structure to locate these pointers. When it
finds one, it removes the pointer from the queue and appends the pointer at the end of another
queue called the freachable queue.

Further, finalizable objects tend to get promoted to the higher generations and hence stay in
memory for a relatively longer period of time. Note that the GC works more frequently in the lower
generations than in the higher ones.

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. This is shown in the following piece of C# code:—

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.

To close out this section, 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.

You might also like