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!
Sorry, none of my geek creds for the first post. Let's get straight to it . . .