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?

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 2
    repro'd this on .net 4.5 beta too – Robert Levy Mar 28 '12 at 17:58
  • Incidentally, I repeated the test, but instead timed how long it took to do `GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();` after all the `new`s. There was no appreciable difference with vs. without the (suppressed) finalizer. – dlf Jul 27 '17 at 20:59

1 Answers1

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.

Brian Rasmussen
  • 114,645
  • 34
  • 221
  • 317
  • 3
    Your imagination is accurate :) – Hans Passant Mar 28 '12 at 23:29
  • 1
    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 different – thecoop Mar 29 '12 at 08:01
  • @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. – Brian Rasmussen Mar 29 '12 at 14:58
  • @Brian Rasmussen why do finalizable types need to be treated differently when they're constructed? Finalization only affects how it gets GCd... – thecoop Mar 30 '12 at 09:39
  • 1
    @thecoop "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. – H H Mar 30 '12 at 10:27
  • @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. – Brian Rasmussen Mar 30 '12 at 14:34
  • @BrianRasmussen - true but that is _another_ queue. Not the one SupressFinalize works on. – H H Mar 31 '12 at 18:18
  • @HenkHolterman that's right. I made it sound like there's just one queue, which isn't the case. Thanks. – Brian Rasmussen Apr 01 '12 at 02:53