Recently, in a tutorial about Weak References in C#, we talked a bit about garbage collection and how the garbage collector works in .NET. I figured since we already started addressing that stuff, there is no reason not to delve deeper. And so, today we are going to take a look at how object finalizers work in C#.
What is an object finalizer? I'm glad you asked! They are essentially the cleanup functions for classes - when an object is collected by the garbage collector in .NET, the finalizer gets run, hopefully cleaning up any unmanaged resources that object may have been holding on to (file references, window handles, network sockets, etc...). An object finalizer is in may ways similar to the C++ destructor, but unlike in C++, a programmer can never call a finalizer directly (in C++, there is the delete operator, and .NET has no such equivalent).
So, first, lets take a look at how to write a finalizer, and then we can delve into the details on when they are run and other caveats.
{
System.Timers.Timer _SillyTimer;
public ClassWithFinalizer()
{
_SillyTimer = new System.Timers.Timer(100);
_SillyTimer.Elapsed +=
(a, b) => Console.WriteLine("Still Alive!");
_SillyTimer.Start();
}
//Finalizer
~ClassWithFinalizer()
{
_SillyTimer.Stop();
_SillyTimer.Dispose();
Console.WriteLine("You Killed Me!!");
}
}
The class in the code block above has a finalizer, and probably by looking at the code you have already figured out the syntax for writing your own. To write a finalizer method, all you do is create a method with the same name as the class (kind of like how you declare a constructor) and you prefix it with a "~". The method takes no arguments, and it does not use the public/private scoping keywords (because a finalizer never gets called explicitly in code anyway).
So what is the above class doing, anyway? Well, its kind of silly, but it shows off the finalizer pretty well. In the constructor we create a timer, and set it so that every 100 milliseconds it prints out the statement "Still Alive!". So when we create this class, "Still Alive!" should print to the console window until the program closes....or at least that is what it would do if there wasn't a finalizer.
When this object gets garbage collected, it stops and disposes the timer, and prints out the final message "You Killed Me!!". Below is some code that causes this behavior to happen, and the corresponding output:
{
new ClassWithFinalizer();
System.Threading.Thread.Sleep(500);
GC.Collect();
}
Still Alive!
Still Alive!
Still Alive!
You Killed Me!!
At the start of the main method, we create an instance of ClassWithFinalizer, but we don't assign the resulting reference to anything. That means that we created the object, but no one is referencing it, so at any point the garbage collector can come along and destroy it. We then sleep the main thread for a bit, possibly letting the instance of ClassWithFinalizer print out "Still Alive" a few times, and then we force a garbage collection by calling GC.Collect(). The garbage collection notices that no one is referencing the instance of ClassWithFinalizer, and so collects it, and in the process executes the finalizer, killing the timer, and printing out the final message of "You Killed Me!!"
What if we didn't have the explicit call to GC.Collect(), and the program just sat there? Well, lets take that line out (and add a Console.Read() to cause the program to sit there):
{
new ClassWithFinalizer();
Console.Read();
}
Still Alive!
Still Alive!
Still Alive!
Still Alive!
Still Alive!
Still Alive!
Still Alive!
Still Alive!
Still Alive!
...
...
...
You Killed Me!!
Who knows how long it would be till the garbage collector finally tried to collect that instance of ClassWithFinalizer? It is quite possible it wouldn't happen until the program closed.
So does that all make sense? Good, cause it is about to become less clear. One of the main caveats of finalizers in .NET is that there are no guarantees about when the finalizer for an object will actually get run. Unlike in C++, where the destructor gets called as soon as an object goes out of scope (and if that's not enough the delete operator can trigger the destructor explicitly), finalizers in C# are not deterministic. A C# finalizer will be called at some point between when the object is last used and the ending of the program - and you as a programmer don't know any more than that.
Hey, and if you look even deeper, it gets yet more complicated - finalizers are not run immediately when the garbage collector gets around to realizing an object can be collected. The garbage collector sees that the object has a finalizer and so adds it to the finalization queue (or f-reachable queue). Eventually, the finalizer method gets run, and then when the garabge collector realizes that, it finally frees the object's memory. So using finalizers can actually delay the real collection on an object for some number of garbage collection cycles.
In a simple example like the one above, everything works as expected. But when you get into bigger programs, this no-deterministic method of finalization can get in the way. The creators of .NET realized this, and so created the Disposable pattern and the using statement, which gives programmers the ability to do much more deterministic object disposal. That, however, is a discussion long enough for another tutorial all on its own. As always, feel free to leave any questions or comments below.
08/29/2008 - 18:09
Is calling .Dispose() not kind of like delete?
08/29/2008 - 20:27
Calling Dispose is not really like delete, because it doesn't actually free the memory of the object. Plus, in order to be able to call dispose, the object has to implement IDisposable and actually present a dispose method. But it is true that implementing Dispose is often better than using an object finalizer - because you can actually call it.
I'm planning on writing a tutorial on IDisposable in the next week or so - so if this comment didn't answer your question, hopefully the soon to be written article will be able to.
01/21/2010 - 10:24
I have modified your code in the main function to better understand the idea and one behavior is blowing my mind away. Wondering if you might be able to help understand this. So here is the code
{
new ClassWithFinalizer();
System.Threading.Thread.Sleep(500);
//GC.Collect();
Console.ReadLine();
}
So like you said there is no reference to ClassWithFinalizer and is a perfect candidate to be grabade collected but with the code above I never get to see “You Killed Me!!”. Is it because I added the Console.Readline() that basically mean this function never go out of scope? I don’t understand it.
However if I uncomment GC.Collect(), I see “You Killed Me!!” pretty much right away.
01/21/2010 - 11:28
The garbage collector won't immediately collect objects that are not referenced. Calling GC.Collect will force it to dispose unreferenced objects. If there's plenty of system memory and your application isn't requiring any more memory, it's actually more efficient for the GC to just leave that object alone.
From the article:
"Who knows how long it would be till the garbage collector finally tried to collect that instance of ClassWithFinalizer? It is quite possible it wouldn't happen until the program closed."
Add Comment
[language] [/language]
Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]
See here for supported languages.
Javascript must be enabled to submit anonymous comments - or you can login.