Ioannis Panagopoulos blog

Tutorials on HTML5, Javascript, WinRT and .NET

Garbage Collection and Object Disposal in C#

by Ioannis Panagopoulos

Let the following be an object handling the retrieval of some data over a database connection:

class DBRetriever

     private List<Customers> _id;
          
     public void GetDataFromDB()
     {
          SQLConnection con=new SQLConnection(...);
          con.Open();
          _id=new List<Customers>();
          // Retrieve the data here
          con.Close();
     }

}

When the application creates a DBRetriever object it reserves memory for it. Note also that the object itself reserves a list of "Customer" objects in memory. When the object is no longer needed the .NET Garbage Collector frees the memory for the object automatically. The garbage collector finds out which object is no longer needed by maintaining object graphs starting from references to objects from the application's variables.

The GC actually does a great job of cleaning up after the programmer. The one thing it cannot handle is resources that have been reserved by unmanaged code. In that case the programmer needs to free those resources on his/her own.

In specific if we implement the method Finalize() in our object the GC knows that when it is about to dispose our object, it should first call the Finalize() method. The strange thing with finalize is that it cannot be overriden as any other method but should be written in the typical "destructor" syntax:

class DBRetriever

     //...     
          
     ~DBRetriever()
     {
            // Unmanaged code mess should be cleaned up here
      }
}

When the code is compiled the destructor is actually translated to the following:

protected override Finalize()
{
     try// Unmanaged code mess should be cleaned up here}
     finally { base.Finalize();}
}
 

So .NET and the CLR Compiler implement from the destructor the method Finalize() for us using a specific try-catch block.

The problem with Finalize() is that it is protected and therefore it is impossible to call it whenever we like. The GC is invoked in undeterministic time instants and therefore we can not rely on it if we want an object to be disposed in a specific moment. But why would we want to do that? The answer is mainly "for improving performance by freeing memory as fast as possible". In these circumstances we implement the IDisposable interface. This interface exposes only ony method Dispose that can be called by the programmer. A typical use of Dispose would be:

//...
SQLConnection conn=new SQLConnection();
conn.Open();
//.....
conn.Dispose();

But wait a minute, aren't we supposed to use Close() instead of Dispose()? Well in some classes the .NET guys decided that Close() seems a more reasonable name than Dispose and therefore, enclosed the same functionality in Close as they did in Dispose. Whenever Close() is supported we can rest assured that there is a Dispose() method that is also supported.

The previous code snippet has a problem. What will happen if the conn.Open() fails? In that case Dispose will never be called. Well, we could enclose the conn.Open() in a try block and dispose the connection in the finally block making sure that whatever happens dispose will be called. Another way that does not make such a mess with the code is the use of the reserved word "using" as follows:

//...
using (SQLConnection conn=new SQLConnection())
{
    conn.Open();
    //.....
}

The use of the keyword "using" means that no matter what will happen within the brackets, at the end the objects instantiated within the parentheses will be disposed (to be more specific the Dispose() method will be invoked for these objects and that is the reason why to support direct disposal we implement the IDisposable interface and not any other abstract method).

Since not all objects and especially the ones we create support the Dispose method, if we want to support this functionality we need to implement in our objects the IDisposable interface. I stress out again that we will do that mainly because our object reserves some resources via unmanaged code.

When we implement the Dispose() method we add an extra way of how an object can be disposed. One is by the Garbage Collector and one is from the User. A way to support both in a small and efficient way without much code replication is perfectly described in the MSDN article navigatable through here.

blog comments powered by Disqus
hire me