Monday, May 18, 2009

Garbage Collector

Ran into a scenario this past week with the GC.

Take this example:

   1: public void Method1()
   2: {
   3:     object o = new object();
   4:     // Do Some work WITH o;
   6:     // Do Some work WITHOUT o;
   7:     o = null;
   8: }
  10: public void Method2()
  11: {
  12:     object o = new object();
  13:     // Do Some work WITH o;
  15:     // Do Some work WITHOUT o;
  16: }

With method is more efficient?

At first glance you would say Method1 since it “cleans up” its variables.  Lets say o was actually a SqlConnection.  We would want to clean that up at the end of the scope right?

Maybe not, turns out the garbage collector in .NET is smarter than us.  In Method2, we don’t reference o after we are done using.  Since it isn’t ever used again the garbage collector will come along and clean it up for you before the method ends.  Pretty slick right?  Declare, use, forget… and I get the optimum effect for free.  Back to o being a SqlConnection, since o “may, if resources are needed or the os has some free time”, the connection will get cleaned up quickly.

Now, for the rub…

I was a good little developer and made sure the cleanup of my RootDisposable cleaned up its ChildDisposable when it was cleaned up.

   1: internal class RootDisposable
   2:     : IDisposable
   3: {
   4:     protected ChildDisposable MyChild { get; private set; }
   6:     public ChildDisposable GetMyChild()
   7:     {
   8:         if(MyChild == null)
   9:             MyChild = new ChildDisposable();
  11:         return MyChild;
  12:     }
  14:     #region IDisposable Members
  16:     protected void Dispose(bool disposing)
  17:     {
  18:         if (MyChild != null)
  19:         {
  20:             MyChild.Dispose();
  21:             MyChild = null;
  22:         }
  23:     }
  25:     public void Dispose()
  26:     {
  27:         Dispose(true);
  28:         GC.SuppressFinalize(this);
  29:     }
  31:     ~RootDisposable()
  32:     {
  33:         Dispose(false);
  34:     }
  36:     #endregion
  37: }

I was also a good developer in that I implemented the Disposal pattern where the Finalizer calls Dispose(false), which will cause my child object to get cleaned up then also.

Now, for the cherry on the sundae, I built my app in Release mode for my customers, deployed it, and it broke for them.  WTF!?  (At this point blame QA, they should have tested the Release version instead of the debug! :D)

Check out the the trunk\Samples\AggressiveGarbageCollection sample in my codeplex site for sample that you can use to play around with this.  Just remember the code will only fail if you build it in Release mode :)

I snagged my GC collect code from example in GC.KeepAlive method in the msdn docs.

No comments: