GC.SuppressFinalize performance compared to non-finalizable object
Asked Answered
M

1

11

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?

Millinery answered 28/3, 2012 at 17:48 Comment(2)
repro'd this on .net 4.5 beta tooWyne
Incidentally, I repeated the test, but instead timed how long it took to do GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); after all the news. There was no appreciable difference with vs. without the (suppressed) finalizer.Demigod
P
8

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.

Photima answered 28/3, 2012 at 21:43 Comment(8)
Your imagination is accurate :)Lettuce
That seems very inefficient. A non-finalizable object should behave exactly the same as a suppressed finalizable object. It is quite suprising that it's differentMillinery
@thecoop: Perhaps, but any type which implements a finalizer will need to be treated differently at construction time. I guess you could make an optimization to check if finalization is suppressed during the constructor, but I don't believe that it is worth the effort. In the common case suppression happens some time after construction of the object.Photima
@Brian Rasmussen why do finalizable types need to be treated differently when they're constructed? Finalization only affects how it gets GCd...Millinery
@Millinery "A non-finalizable object should behave exactly the same as a suppressed finalizable object." - No. Try to find a ref for it, you won't. It is explicitly stated that having a dtor adds (some) overhead when instancing.Rapeseed
@thecoop: Because GC cannot collect the instance before the finalizer has run. To make this work the instance is put on the queue (which then roots the object) and thus GC will not collect it. When the finalizer has run or suppress is explicitly called it will be removed from the queue and then GC can do its job.Photima
@BrianRasmussen - true but that is another queue. Not the one SupressFinalize works on.Rapeseed
@HenkHolterman that's right. I made it sound like there's just one queue, which isn't the case. Thanks.Photima

© 2022 - 2024 — McMap. All rights reserved.