Using Close Dispose and Finalizers

Saturday, June 28 2008

Summary

[this was written against the 1.1 framework]

At some point in your use of .NET you are going find that garbage collection, or rather, the lack of garbage collection is causing you a problem. The most likely scenario is that you have some unmanaged resource that you are trying to access and you can’t. Fortunately Microsoft provides us with the tools to handle these types of situations. In this article I’ll be discussing Finalization, Dispose and the IDisposable pattern, Close and the C# specific Using statement. I will also touch on some garbage collection terms. If you aren’t familiar with any of the terms used I strongly suggest you get hold of a good book on CLR Internals and gain an understanding. The .NET garbage collector can be your friend or your worst enemy.

 
Finalize
Finalization in .NET is the process by which the garbage collector [GC] allows objects to clean up any unmanaged resources prior to destroying the object. When the GC determines an object is eligible for garbage collection it will call that objects finalize method. It is up to that method to close any unmanaged resources. If you have an object that is holding unmanaged resources, but don’t provide a finalize method then you will cause a memory leak in your application as resources will be orphaned with no way to free them.
When an object that is finalizable is created a pointer to it is put on the Finalization queue. When the GC runs looking for objects that no longer have any references to them it also checks the Finalization queue. If an object is in the Finalization queue then that object is not garbage collected. Instead the pointer is removed from the Finalization queue and its Finalize method will be called. The actual call is made by a special Finalization thread. On the next GC the object will be eligible for collection. That doesn’t mean that it will be collected. It is possible for a finalizable object to be moved to a later generation, causing it to live even longer.
This means that finalizable objects take longer to allocate and cost significantly more to free. Make sure you really need that finalizer before you create it.
Declaring a Finalize Method
There are two way to declare a finalize method. Unfortunately if you chose the one below you are given a compiler exception that does more harm that good.
 
Finalizer Example1
      public class FinalizeExample
      {
            private FileStream _fileStream;
            private IntPtr _fileHandle;
            private IntPtr _invalidPtr = IntPtr.Zero;
 
            public FinalizeExample()
            {
                  _fileStream  = File.Create("c:\temp\testfile.txt");
                  _fileHandle = _fileStream.Handle;
            }
            public void WriteToStream(byte logByte)
            {
                  _fileStream.WriteByte(logByte);
        }
            protected override void Finalize()
            {
                  _fileHandle = _invalidPtr;
                  _fileStream.Close();
            }    
      }
If you compile this the IDE will give you an error message:
Do not override object.Finalize.  Instead, provide a destructor.
The Finalizer is NOT a destructor. Ignore the fact that someone at Microsoft is telling you it is. Actually for whatever reason MS chose to call the finalizer a destructor, but it is confusing so don’t think of it that way!
 
Destructor vs. Finalizer
 
A destructor is something that deterministically releases resources [e.g. C++ destructor]. A finalizer is NOT deterministic. The finalize method only gets called when GC happens. I’ll say it again…. The finalize method only gets called when GC happens.
Here’s how you declare the finalizer correctly
 
      public class FinalizerExampleOkay
      {
            private FileStream _fileStream;
            private IntPtr _fileHandle;
            private IntPtr _invalidPtr = IntPtr.Zero;
            public FinalizerExampleOkay()
            {
                  _fileStream  = File.Create("c:\temp\testfile.txt");
                  _fileHandle = _fileStream.Handle;
            }
            public void WriteToStream(byte logByte)
            {
                  _fileStream.WriteByte(logByte);
        }
            ~FinalizerExampleOkay()
            {
                  _fileHandle = _invalidPtr;
                  _fileStream.Close();
            }
      }
It sure looks like a destructor doesn’t it?
 
Finalizer Summary
 
So the finalizer is a way for you to ensure that resources are released when an object is garbage collected. It is NOT a deterministic release of those resources.
  • The finalize method is not free, rather it is an expensive operation.
  • Objects that have finalize methods are promoted to the next generation when the GC determines they are garbage. They are not eligible to be collected until the next GC run.
  • Any objects referenced by the object with the finalize method will be promoted along with the object, which means you will be allocating and holding resources longer than necessary.
  • Finalization can be called in any order the CLR chooses. This means that you should never access another object that implements a finalizer from your finalizer. If you do the other object could be finalized first leaving you with a null reference.
  • Finalize method will be called even if your object’s constructor threw an exception.
 
More Reading
 
Dispose via IDisposable
 
We’ve talked about how to use a Finalize method to clean up your resources. However, the problem with this method is that you can’t call it. It is called by the .NET Framework for you. In order to allow more control over the release of unmanaged resources MS introduced the concept of a Dispose method. They also introduced the IDisposable interface to indicate that a class has a Dispose method available.
Any class that implements IDisposable MUST
·         Implement a finalizer. The reason for this is that Dispose must be called directly by some code. The finalizer exists to handle any case where some code failed to call dispose properly.
·         Call its base class Dispose method (if one exists)
·         Call GC.SupressFinalize in Dispose() to prevent GC from unnecessary finalization steps
·         If called more than once it must not throw an exception
·         May throw an exception if the resource it was attempting to free was already freed.
 
IDisposable Example
 
    public class DisposeExample
    {
        private FileStream _fileStream;
        private IntPtr _fileHandle;
        private IntPtr _invalidPtr = IntPtr.Zero;
        private bool _disposed = false;
 
        public DisposeExample()
        {
            _fileStream  = File.Create("c:\temp\testfile.txt");
            _fileHandle = _fileStream.Handle;
 
        }
        public void WriteToStream(byte logByte)
        {
            _fileStream.WriteByte(logByte);
        }
        /// <summary>
        /// Finalizer to ensure our objects resources are
        /// freed
        /// </summary>
        ~DisposeExample()
        {
            Dispose(false);
        }
        /// <summary>
        /// IDisposable interface implimentation to
        /// </summary>
        public void Dispose()
        {
            //pass true indicating managed resources can be freed as well e.g. our code called
            //dispose instead of the .NET framework
            Dispose(true);
            //prevent Finalization since we already freed the resources
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// Virtual Dispose method that any subclasses will inherit and
        /// override.
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if(!_disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if(disposing)
                {
                    // Dispose managed resources.
                    _fileStream.Close(); //note we called close because it was available
                }
                // Release unmanaged resources. If disposing is false,
                // only the following code is executed.
                _fileHandle = _invalidPtr;
                //thread safety is up to the client!
            }
            _disposed = true;
        }
    }
 
Derived Class
 
A derived class only needs to implement the virtual Dispose method. Like the base class it needs to determine if it can free managed resources (dispose == true) or not. It must also call the base class Dispose to ensure that all resources are freed.
 
Derived Example
 
public class DisposeExampleDerived:DisposeExample
    {
        private bool _disposed = false;
        private IntPtr _unmanagedHandle;
        private IntPtr _invalidPtr = IntPtr.Zero;
        private FileStream fs = new FileStream(@"C:\temp\tempstream.txt", FileMode.Append);
 
        public DisposeExampleDerived(){}
        /// <summary>
        /// Virtual Dispose method that any subclasses will inherit and
        /// override.
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if(!_disposed)
            {
                //notice we use try/finally to ensure base.Dispose gets called
                try
                {
                    if(disposing)
                    {
                        // Release the managed resources you added in
                        // this derived class here.
                        fs.Close(); //close because they provided close instead of dispose
                    }
                    // Release the native unmanaged resources you added
                    // in this derived class here.
                    _unmanagedHandle = _invalidPtr;
                    _disposed = true;
                }
                finally
                {
                    // Call Dispose on your base class.
                    base.Dispose(disposing);
                }
 
            }
 
        }
 
 
    }
 
Further Reading
 
 
Close
 
Close is essentially a method of convenience. It was apparently added because someone thought developers are more likely to call Close() than Dispose(). The recommendation is that if it makes more sense to call Close on something [e.g. a stream object] then implement a Close method and have it call Dispose.
 
I find the use of the Close method to be just added confusion. If you look closely at the IL for various methods that have a Close method you will see that MS apparently wasn’t really sure about it either. The best thing you can do is to follow the “recommendation”, which is having your Close method call Dispose.
 
Some Confusing Examples
 
SqlDataReader:Close()
The Close() method on the SqlDataReader calls an InternalClose() method, which does not call Dispose. Note that previously we stated the correct way to do this was to have your close call dispose. To make it even more confusing the Dispose() method actually calls the Close() method so for this object the order is reversed.
 
SqlDataAdapter()
This class only contains the Dispose(bool disposing) override. So even in the same namespace we see differences.
 
 
 
 
 
 
 
What do I call
There has been a lot of discussion around what you should call: Close, Dispose or both. If you follow the recommendations from MS about the IDisposable pattern it shouldn’t matter. Close should do exactly the same thing that Dispose does. I don’t call either as I use the Using statement whenever IDisposable is implemented.
 
Using
 
C# has a very convenient way to ensure that a method with IDisposable gets its Dispose method called.
The Using statement. According to MSDN the Using Statement defines a scope at which an item will be disposed.
 
Using Example
 
        public void UsingCalled()
        {
            using (SqlCommand cmd = new SqlCommand("sp_something", new SqlConnection("string")))
            {
                //always supply the commandBehavior!
                SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                while(reader.Read())
                {
                    //do something
                }
 
            } //this will call close/dispose
        }
 
IL Using Statement
 
.method public hidebysig instance void  UsingCalled() cil managed
{
  // Code size       53 (0x35)
  .maxstack  3
  .locals init ([0] class [System.Data]System.Data.SqlClient.SqlCommand cmd,
           [1] class [System.Data]System.Data.SqlClient.SqlDataReader reader)
  IL_0000:  ldstr      "sp_something"
  IL_0005:  ldstr      "string"
  IL_000a:  newobj     instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
  IL_000f:  newobj     instance void [System.Data]System.Data.SqlClient.SqlCommand::.ctor(string,
                                                                                          class [System.Data]System.Data.SqlClient.SqlConnection)
  IL_0014:  stloc.0
  .try
  {
    IL_0015:  ldloc.0
    IL_0016:  ldc.i4.s   32
    IL_0018:  callvirt   instance class [System.Data]System.Data.SqlClient.SqlDataReader [System.Data]System.Data.SqlClient.SqlCommand::ExecuteReader(valuetype [System.Data]System.Data.CommandBehavior)
    IL_001d:  stloc.1
    IL_001e:  br.s       IL_0020
    IL_0020:  ldloc.1
    IL_0021:  callvirt   instance bool [System.Data]System.Data.SqlClient.SqlDataReader::Read()
    IL_0026:  brtrue.s   IL_0020
    IL_0028:  leave.s    IL_0034
  }  // end .try
  finally
  {
    IL_002a:  ldloc.0
    IL_002b:  brfalse.s  IL_0033
    IL_002d:  ldloc.0
    IL_002e:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0033:  endfinally
  }  // end handler
  IL_0034:  ret
} // end of method UsingSample::UsingCalled
 
Notice how in the above IL the Using Statement was converted into a Try…Finally to ensure our object gets its Dispose method called.
 
Example 2 Call Close
 
        public void CloseCalled()
        {
            SqlCommand cmd = new SqlCommand("sp_something", new SqlConnection("string"));
            SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
 
            while(reader.Read())
            {
                //do something
            }
 
            reader.Close();
        }
 
IL Close
 
.method public hidebysig instance void  CloseCalled() cil managed
{
  // Code size       47 (0x2f)
  .maxstack  3
  .locals init ([0] class [System.Data]System.Data.SqlClient.SqlCommand cmd,
           [1] class [System.Data]System.Data.SqlClient.SqlDataReader reader)
  IL_0000:  ldstr      "sp_something"
  IL_0005:  ldstr      "string"
  IL_000a:  newobj     instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
  IL_000f:  newobj     instance void [System.Data]System.Data.SqlClient.SqlCommand::.ctor(string,
                                                                                          class [System.Data]System.Data.SqlClient.SqlConnection)
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  ldc.i4.s   32
  IL_0018:  callvirt   instance class [System.Data]System.Data.SqlClient.SqlDataReader [System.Data]System.Data.SqlClient.SqlCommand::ExecuteReader(valuetype [System.Data]System.Data.CommandBehavior)
  IL_001d:  stloc.1
  IL_001e:  br.s       IL_0020
  IL_0020:  ldloc.1
  IL_0021:  callvirt   instance bool [System.Data]System.Data.SqlClient.SqlDataReader::Read()
  IL_0026:  brtrue.s   IL_0020
  IL_0028:  ldloc.1
  IL_0029:  callvirt   instance void [System.Data]System.Data.SqlClient.SqlDataReader::Close()
  IL_002e:  ret
} // end of method UsingSample::CloseCalled
 
Notice how there is no Try…Finally in this one and we actually call Close not Dispose.
 
Example 3 Call Nothing
 
        public void NothingCalled()
        {
            SqlCommand cmd = new SqlCommand("sp_something", new SqlConnection("string"));
            SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            while(reader.Read())
            {
                //do something
            }
        }
 
IL Call Nothing
 
.method public hidebysig instance void  NothingCalled() cil managed
{
  // Code size       41 (0x29)
  .maxstack  3
  .locals init ([0] class [System.Data]System.Data.SqlClient.SqlCommand cmd,
           [1] class [System.Data]System.Data.SqlClient.SqlDataReader reader)
  IL_0000:  ldstr      "sp_something"
  IL_0005:  ldstr      "string"
  IL_000a:  newobj     instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
  IL_000f:  newobj     instance void [System.Data]System.Data.SqlClient.SqlCommand::.ctor(string,
                                                                                          class [System.Data]System.Data.SqlClient.SqlConnection)
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  ldc.i4.s   32
  IL_0018:  callvirt   instance class [System.Data]System.Data.SqlClient.SqlDataReader [System.Data]System.Data.SqlClient.SqlCommand::ExecuteReader(valuetype [System.Data]System.Data.CommandBehavior)
  IL_001d:  stloc.1
  IL_001e:  br.s       IL_0020
  IL_0020:  ldloc.1
  IL_0021:  callvirt   instance bool [System.Data]System.Data.SqlClient.SqlDataReader::Read()
  IL_0026:  brtrue.s   IL_0020
  IL_0028:  ret
} // end of method UsingSample::NothingCalled
 
Notice how we are left to the mercy of the GC with this example. We don’t call close explicitly which means our SqlConnection object won’t close for use via the CommandBehavior.
 
There are some subtle things to watch out for when you use the Using Statement. You must make sure that you set the scope correctly. If you don’t you could end up with your object being destroyed either well before you are ready for that to happen, or far too late to do you any real good.
 
There are a lot of details around Finalizers and how the GC works that easily fill a book (in fact a few have been written). I encourage you to take a closer look at the CLR, download Rotor, get an Internals book and most of make sure you understand what your code is really doing.