CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Grant Killian's Blog

No, this has nothing to do with beer -- but maybe it should?

Don't Dispose() of the GC!

I'm teaching a .Net class Monday which covers, among other things, CLR Garbage Collection.  I've taught the topic before and invariably the .Net implementation of Garbage Collection rubs some experienced developers the wrong way.  Non-deterministic finalization is the fancy term for not knowing when an object will be removed from memory.  It means that you can't rely on clean-up logic (releasing database connections, closing files, etc.) running at a specific time -- like when an object simply goes out of scope.  This means you have to take care in writing your clean-up logic.  Programmers from non-.Net backgrounds don't like the unreliable Destructor behavior.

Our class uses the Microsoft .Net self-paced traning kit as teaching material (ISBN 0-7356-1533-0), and they provide a lab for learning about basic garbage collection; we can start with their example.  You create a class as follows:
Public Class Demo
 Public Shared Instances as Long
 Public Sub New()
  Instances += 1
 End Sub   
 Protected Overrides Sub Finalize() 'called when GC runs
  Instances -= 1
 End Sub
End Class

Then you create a WinForms application as follows:
 1 Button Control
 1 Label Control
 1 Timer Control

Add the following to the OnClick() button event handler:
 Dim ctr as Integer
 for ctr = 1 to 1000
  obj = new Demo()
 Next

Add the following to the Tick() timer event (be sure to set the Enabled property to True because Timers default to being Disabled):
 Label1.Text = "Object Instances in Memory: " & Demo.Instances

If you run this app and click the button enough times, you'll create enough Demo objects in memory to eventually trigger a Garbage Collection cycle (if your computer is decked out with lots of memory, it may take quite a few clicks -- I can create 30,000+ object instances). 

The moral of the story: nobody can guarantee when an object will be removed from memory which means you shouldn't put "must run" clean-up code in the Finalize() method.  When objects go out of scope, they aren't automatically reclaimed by the CLR; instead, they are reclaimed whenever the Garbage Collection process runs (which is on a low-priority thread).

The textbook stops there, which often sours experienced developers on the CLR Garbage Collection service because they can't control object behaviour the way they can in other environments.  C# is the same way, so this can't be chalked up as a VB.Net oddity.  Don't get down on the GC, though, just because the book leaves it at that!  Let's modify the example and have some real geek fun. 

If we implement the IDisposable interface, some interesting things happen.  Consider the following DemoDisposable class:
Public Class DemoDisposable
 Implements IDisposable

 Public Shared Instances as Long
 Public Sub New()
  Instances += 1
 End Sub   
 Protected Overrides Sub Finalize()
  Instances -= 1
 End Sub
   
 Public Sub Dispose() Implements System.IDisposable.Dispose
  Finalize()
  System.GC.SuppressFinalize( me )
 End Sub
End Class

We've added an "Implements IDisposable" statement to the class definition, which means our class will conform to the IDisposable interface.  I'm tempted to get into interfaces and programming by contract here, but that's a subject for another time . . .  Also note the Sub Dispose() method in our new class; a Dispose() method is required by the IDisposable interface.  The System.GC.SuppressFinalize( me ) statement prevents the object from being Finalized a second time by the Garbage Collector (which is unnecessary since we've explicitly done it in the Dispose() method -- it's an optimization).

In our Windows Application, we could write our button onClick() event handler code as follows:
 Dim ctr as Integer
 dim objDis as DemoDisposable
 for ctr = 1 to 10000
  objDis = new DemoDisposable()
  'do something with objDis . . .
  objDis.Dispose()
 Next

We've added a call to the Dispose() method which will perform the clean-up for us DETERMINISTICALLY.  Now run the program and note that your objects aren't kept around after the Dispose() method is called.

This approach is called "The Dispose Pattern." 

Granted, this Dispose Pattern isn't ideal as programmers must remember to always call Dispose() on objects.  There's no guarantee Dispose will be called (in which case normal non-deterministic Garbage Collection runs its course), but it's a lot better than nothing!

As they say on the Home Shopping Network (I know a guy who programs for them, by the way): but wait, there's more:

If you're into C# you can take advantage of the using statement.  If you port the example code to C# you can replace the button onClick event handler code with the following:
 int ctr;
 DemoDisposable objDis;
 for( ctr = 1; ctr< 10000; ctr++ ) {
  using( objDis = new DemoDisposable() ) {
   //do something with DemoDisposable  
  }
 }
The using statement guarantees implicit calling of the Dispose() method so you don't have to!  There are some other good points to the using statment, but this post is long enough already.  Note that the VB.Net language doesn't have anything comparable to a using statement; score 1 for C#.

There are some other ways to acheive deterministic finalization, but I'll leave it at that for the time being.

Hopefully your personal GC process won't destroy the substance of this post the next time you're wrestling with .Net object destruction.  Happy .Netting!



Check out Devlicio.us!