Sunday, 23 October 2011

object.Finalize()

disclaimer: I don't usually spell finalize with a 'z' (pronounced 'zed'), but I have done so throughout this article to keep things consistent with official documentation, and so as not to confuse any of our friends from the U.S.


The Finalize() Method
C# appears to allow the use of destructors, but under the hood code that looks like this:
class FinalizeClass
{
    ~FinalizeClass()
    {
        // Do stuff
    }
}
actually gets compiled into code like this:
class FinalizeClass
{
    protected override void Finalize()
    {
        try
        {
            // Do stuff
        }
        finally
        {
            base.Finalize();
        }
    }
}
The Finalize() method is a special method that you as a developer can't override - it is created by the compiler from the definition of a destructor. It can only be called by the GC.

Creating a Finalizable Object
When the GC allocates the memory for an object that implements Finalize(), it is not created as generation 0, but as an older generation. This is also true for all objects that the finalizable object references. This is an optimisation, as there will be more to do when it eventually comes to reclaim the memory used.
On creation, the GC puts a reference to the object on the Finalization queue; this queue is effectively a list of objects that need Finalize() called on deallocation.

GC of a Finalizable Object
When the GC calculates that an object is unreachable, it searches for a reference to that object on the finalization queue; if found, the reference is moved to a different queue, the "freachable" queue (F-reachable). This is a list of objects that are ready to have their Finalize() methods called.
At this stage, the object has not had its memory reclaimed; this is done by a thread dedicated to the purpose of calling Finalize() methods. Only when this has been done is the object truly unreachable, so it takes another round of GC for its memory to be fully reclaimed.

Guaranteed Order of Execution?
There is no guarantee of the order that Finalize() methods are called: if ObjectA has a reference to ObjectB, ObjectB's Finalize() may already have been called by the time ObjectA's Finalize() is called. ObjectB is still available to ObjectA, but has already been finalized so may produce unexpected results.

Resurrection of a Dead Finalizable Object
When an object is unreachable, it is assumed to be dead by the GC. In the normal course of events, it would be resurrected in order to call its Finalize() method, then die again. However, consider the following code:
public class MyClass
{
    ~MyClass()
    {
        // This resurrects the object
        StaticClass.StaticObjectReference = this;
    }
}
The class creates a reference to itself in a global static variable. This resurrects the object, as it is now reachable; however, its Finalize() method has been called, so using the object could cause unexpected results. The resurrection of the MyClass object also causes the resurrection of each object it references, but these may also have been finalized...

GC.ReRegisterForFinalize()
If the object is resurrected intentionally during its finalization, the developer is able to ensure that this "lifetime" will be finalized too, by calling GC.ReRegisterForFinalize():
public class MyClass
{
    ~MyClass()
    {
        // This resurrects the object
        StaticClass.StaticObjectReference = this;


        // This ensures the object will be finalized 
        // next time it is collected
        GC.ReRegisterForFinalize(this);
    }
}
Beware of calling GC.ReRegisterForFinalize() multiple times, as each call will cause an eventual call to the Finalize() method.

GC.SuppressFinalize()
If your object implements a Finalize() method but also has an alternative method of releasing resources (for example, Dispose(), Close() or something similar), an unnecessary Finalize() call can be avoided by a call to GC.SuppressFinalize(). This informs the GC that the object does not require its Finalize() method called.
This can be useful where an object implements both the IDisposable interface and Finalize(). Dispose() allows for deterministic deallocation of resources, and the Finalize() method is implemented as a safety net. e.g.
public class MyClass : IDisposable
{
    private bool disposed = false;


    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose().
                // OK to use any private object references
            }
            disposed = true;
        }
    }


    public void Dispose() // Implementation of IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}
Reference articles:
MSDN Library article on Garbage Collection

MSDN magazine articles:
Automatic Memory Management in the Microsoft .NET Framework, Part 1
Automatic Memory Management in the Microsoft .NET Framework, Part 2

StackOverflow
StackOverflow - When should I use GC.SuppressFinalize()?

Next time: System.GC - an explanation of those hard-to-understand methods

No comments:

Post a Comment