Is a finalizable object with GC.SuppressFinalize the same as a normal unfinalizable object? The code below seems to prove they're treated differently, both on .NET 2 and 4:
class Class1 {
public Class1()
{
GC.SuppressFinalize(this);
}
//~Class1() { }
}
class Program
{
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i=0; i<100000000; i++)
{
new Class1();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
Adding the finalizer, but not changing anything else, causes the code to take far far longer (12601 ms compared to 889 ms).
I thought SuppressFinalize set a bit in the object header making the GC treat the object the same as a non-finalizable object, but this does not seem to be the case. So what's going on? What is different between a non-finalizable object and a finalizable object with GC.SuppressFinalize called on it?
Once you disposed the object, you should indeed call GC.SuppressFinalize (this);, like shown in an answer to the question "When should I use GC.SuppressFinalize ()?". Code analysis' CA1816 rule is also useful as well. Now, implementing IDisposable on objects which don't have unmanaged resources looks quite weird and questionable.
GC keeps a list (Finalization Queue) of all objects the class of which declares a Finalizer (~ClassName in C#). Objects are put in this queue upon creation. GC runs periodically to check whether there are any objects inaccessible from the program.
The SuppressFinalize method simply sets a flag in the object header which indicates that the Finalizer does not have to be run. This way GC can reclaim the memory of the object right away.
This means that a finalizable object incurs an extra cost: the object must be kept around in memory for a longer time. Hence the call you see: it is worthwhile to suppress finalization when it is not needed. Here, the object uses finalization to ensure that it is always "disposed of" at some point.
As I understand it the CLR has a queue of objects for which finalization has been registered. Implementing a finalizer will put objects of the type on the queue. So in the case where the constructor calls SuppressFinalize
, I imagine that the object is actually put on the queue only to be removed immediately, which could explain the overhead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With