#430 – A Dispose Pattern Example

If you want to control when an object’s unmanaged resources are released, you can follow the dispose pattern, implementing a Dispose method.

Here’s a complete example.  We create a method to release resources that is called either when a client invokes Dispose directly or when the CLR is finalizing the object.

    public class Dog : IDisposable
    {
        // Prevent dispose from happening more than once
        private bool disposed = false;

        // IDisposable.Dispose
        public void Dispose()
        {
            // Explicitly dispose of resources
            DoDispose(true);

            // Tell GC not to finalize us--we've already done it manually
            GC.SuppressFinalize(this);
        }

        // Function called via Dispose method or via Finalizer
        protected virtual void DoDispose(bool explicitDispose)
        {
            if (!disposed)
            {
                // Free some resources only when invoking via Dispose
                if (explicitDispose)
                    FreeManagedResources();   // Define this method

                // Free unmanaged resources here--whether via Dispose
                //   or via finalizer
                FreeUnmanagedResources();

                disposed = true;
            }
        }

        // Finalizer
        ~Dog()
        {
            DoDispose(false);
        }
    }

#429 – Use the Dispose Pattern for Deterministic Destruction

You can implement a finalizer in a class to explicitly release unmanaged resources before an object’s memory is freed.  Drawbacks of this approach include:

  • You don’t control when the object’s resources are cleaned up
  • A finalizer can’t make use of any other managed objects  (they may have already been finalized)

Another pattern for releasing unmanaged resources is for a class to implement the IDisposable interface by providing a Dispose method.  Dispose is meant to clean up the object’s resources when it’s called.  A client of the class can decide exactly when it wants to release the object’s resources (deterministic destruction).  Benefits of this pattern include:

  • Can release resources earlier, not waiting for finalization
  • Dispose logic can make use of other managed resources
    public class Dog : IDisposable
    {
        public void Dispose()
        {
            // Release unmanaged resources here

            // Note: need logic to prevent double disposal, etc.
        }

#428 – A Finalizer Should Always Call the Finalizer of Its Base Class

An implementation of Object.Finalize for a particular class in .NET should always call the Finalize method of its base class.  In the case of C#, you can’t explicitly override Finalize, but you provide a finalizer using the destructor (~) syntax.  When you do this, the compiler will automatically add code to ensure that your destructor calls Finalize in the base class.

In the example below, I write a simple destructor for the Dog class.  We can see in the IL that the Finalize method has been overridden and that it calls System.Object.Finalize in the finally clause.

        ~Dog()
        {
            Trace.WriteLine("This dog is on the way out..");
        }

#427 – Finalizer Gotchas

The idea of the finalizer in .NET is that you can override the Object.Finalize method, called by the garbage collector when it’s releasing memory for an object, and you can do any required cleanup of unmanaged resources.

There are several things to keep in mind if you want to write a finalizer (done in C# using the destructor syntax):

  • You can’t predict when your finalizer is called
  • The finalizer might never get called at all
  • The order that finalizers are called in is unpredictable.  You therefore can’t assume that any other managed object that your object references still exists, or is in a known state.
  • Garbage collection will take longer if you have objects with finalizers

 

#426 – Use a Destructor to Free Unmanaged Resources

When an object is no longer referenced by other objects, its memory may be reclaimed by the garbage collector in the CLR.  If you have any unmanaged resources that you need to clean up explicitly, you can implement a finalizer for your class.

A finalizer is the implementation of the Object.Finalize method for your class, which the garbage collector calls when it’s ready to release memory for an object.  In C#, you implement a finalizer using the destructor syntax–a method defined as a tilde (~), followed by the name of the class.

    public class Dog
    {
        ~Dog()
        {
            Trace.WriteLine("This dog is on the way out..");
        }

When you look at the IL for this class, you’ll see that the compiler has overridden the Finalize method.

You’ll notice that this method is now called when the garbage collector is releasing the object.

#425 – Nondeterministic Destruction and Object Finalization

In .NET, the CLR (Common Language Runtime) is responsible for reclaiming memory for objects that are no longer being referenced, through garbage collection.

Garbage collection can be done on an object as soon as it’s no longer being referenced by any other objects.  However, the garbage collector may not free memory for an object immediately.  It can choose to free up memory for an object whenever it likes.

Every object has a Finalize method that you can override, which will be called when the object is being destroyed.  This method allows cleaning up any unmanaged resources the object might hold, before the object is destroyed.

The CLR implements nondeterministic destruction–you can’t explicitly force an object’s Finalize method to be called and you can’t predict when it is called.  There’s also no guarantee that the finalizer will ever be called–an application might crash before the finalizer is called.

#424 – The Garbage Collector

In C#, you don’t explicitly delete heap-based objects.  Instead, the CLR will reclaim memory from objects that are no longer used, by running a Garbage Collector.

The garbage collector (GC) can release memory only for managed objects created on the heap.  It’s only job is to release memory.  It can’t clean up or release other resources, like file handles or database connections.

The GC will typically perform garbage collection when the amount of available physical memory is becoming too low or when the amount of virtual memory consumed by your application is becoming too large.

You can’t predict when garbage collection is going to happen, or even if it’s going to happen, prior to an application terminating.