When everything you know is wrong, part one

Finalizers are interesting and dangerous because they are an environment in which everything you know is wrong. I’ve written a lot about the perils of C# finalizers / destructors (either name is fine) over the years, but it’s scattered in little bits over the internet. In this series I’m going to try to get everything in one place; here are a bunch of things that many people believe about finalizers, all of which are wrong.

First off, since this is a blog and not a suspense novel, let me briefly describe “normal” — and I use the term guardedly — finalization semantics in the CLR.

  • The GC runs (on the GC thread) and identifies that an object has no more living references.
  • If the object has a finalizer and is a candidate for finalization, it goes on the finalization queue instead of being reclaimed.
  • The finalizer thread calls finalizers on objects on the queue, removes them from the queue, and marks them as “not candidates for finalization”.
  • The GC runs again; the objects that were dead before are still dead, but this time they are no longer candidates for finalization, so their memory is reclaimed.

That brief sketch glosses over many interesting corner cases, some of which we’ll look at in this series. This series should not be considered a complete list of everything that can go wrong with finalizers. If you want a deeper, more accurate, and historically interesting look at how finalizers were designed in the early days of .NET, see cbrumme’s seminal 2004 post on the subject.

In this episode we’ll examine false beliefs that people have about when a finalizer is required to run.

Myth: Setting a variable to null causes the finalizer to run on the object that was previously referenced by the variable.

foo = null; // Force finalizer to run

Setting a variable to null does not cause anything to happen immediately except changing the value of the variable. If the variable was the last living reference to the object in question then that fact will be discovered when the garbage collector runs the collector for whatever generation the object was in. (If it runs at all, which it might not. There is no guarantee that the GC runs.)

Myth: Calling Dispose() causes the finalizer to run.

foo.Dispose(); // Force finalizer to run

Dispose() is nothing special to the runtime; the convention that we call a method named Dispose() when an object has resources to clean up is not encoded into the runtime. It’s just a method.

The standard pattern for implementing Dispose() on an object with a finalizer is: both the Dispose() method and the finalizer should call a method Dispose(bool), where the flag is true if the caller is Dispose() and false if it is the finalizer. The Dispose(bool) method then does the work of both the Dispose() method and the finalizer, as appropriate.

Myth: Calling GC.Collect() causes finalizers to run.

No, it causes a collection to happen. That might identify objects that are candidates for finalization, but it does not force the finalizer thread to be scheduled. If that’s what you want — for testing purposes only please! — then call GC.WaitForPendingFinalizers().

Myth: Finalizers are guaranteed to run for every finalizable object.

The SuppressFinalize method causes finalization to be suppressed, hence its name. So there is no guarantee that a given object will be finalized; its finalization could be suppressed. (And in fact it is common for Dispose() to suppress finalization, so that the object’s memory can be reclaimed earlier. Note that in the sketch above, a finalizable object needs to be detected by the GC twice before its memory is reclaimed. We’d like objects where Dispose() was called to get reclaimed after only one collection.)

Myth: Finalizers are guaranteed to run eventually for every finalizable object where finalization has not been suppressed.

Suppose there is an object with a finalizer that is allocated; a reference to the object is placed into a static field, and the program then goes into an infinite loop. Objects referenced by static fields are alive, and therefore the garbage collector never detects that the object is dead, and therefore never puts it on the finalizer queue.

Myth: Finalizers are guaranteed to run eventually if there are no more living references to an object.

The garbage collector places objects that are no longer referenced onto the finalizer queue, but that requires that the garbage collector run. The last reference to a finalizable object could be in a local variable, and when the method returns, the object is no longer referenced. The program could then go into an infinite loop which allocates no memory, and therefore produces no collection pressure, and therefore the garbage collector never runs.

Myth: Finalizers are guaranteed to run at some time after the garbage collector has determined that an object is unreachable.

In that case the garbage collector will put a reference to the object onto the finalization queue. But the finalizer thread is a separate thread, and it might never be scheduled again. There could always be a higher-priority thread.

Myth: Finalizers of objects awaiting finalization are guaranteed to run provided that the finalizer thread has been scheduled to run.

There is no guarantee that the finalizer thread will get to all the objects in the finalization queue. A number of situations, such as a finalizer taking too long, a finalizer throwing an exception, a finalizer deadlocking, an AppDomain being unloaded, the process failing fast or someone simply pulling the power cord out of the wall can cause finalization to never happen for an object in the finalizer queue.

Myth: Finalizers of objects awaiting finalization are guaranteed to run provided that the GC runs, and the GC identifies a finalizable object, and the finalizer thread has been scheduled to run, and the finalizer is fast, and the finalizer does not throw or deadlock, and no one pulled the power cord out of the wall.

Nope! Here we produce collection pressure, so the GC is running, we are identifying finalizable objects, and clearly the finalizer thread is running because we get output. Care to predict the behavior of this program?

class Weird
{
    static int count = 0;
    ~Weird()
    {
        count += 1;
        System.Console.WriteLine(count);
        new Weird();
    }
    static void Main()
    {
        new Weird();
    }
}

The program terminates without error after having created just over 14000 objects on my machine. That means that one of those objects was never finalized.

What is going on here? When finalizers are being run because a process is being shut down, the runtime sets a limit on how much time the finalizer thread gets to spend making a good-faith effort to run all the finalizers. If that limit is exceeded then the runtime simply stops running more finalizers and shuts down the program. (Again, a good historical overview of how this was designed can be found here. I don’t have a more up-to-date exegesis handy.)

All of the myths so far imply that if you have code that absolutely, positively must run because it has some important real-world impact then a finalizer is not your best choice. Finalizers are not guaranteed to run.

Next time on FAIC: Even more myths about finalizers.

Advertisements

43 thoughts on “When everything you know is wrong, part one

  1. You can get some properly weird behaviour by running the nearest equivalent of this program in LinqPad. On my machine, the number of lines of output increases by 4 every time. Running, adding whitespace to the code, and running again gave the output:
    5
    1
    2
    6
    7
    3
    4
    8

    • Linqpad has a potential bug around static variables. If the program being run doesn’t change, values of static variables are retained across executions.

      If you run this program repeatedly without changes:

      static int i = 0;

      void Main()
      {
      i.Dump();
      i++;
      }

      you can see that i is incremented by one on each run. If you make a change (including deleting and re-creating a curly brace) it will reset.

      You can see some especially interesting Linqpad implementation details with this program:

      class Weird
      {
      static int ctorCount = 0;
      static int dtorCount = 0;
      int id;

      public Weird(int id)
      {
      this.id = id;
      ctorCount++;
      System.Console.WriteLine(“Id: {0}, Ctor: {1}”, id, ctorCount);
      }

      ~Weird()
      {
      dtorCount++;
      System.Console.WriteLine(“Id: {0}, Dtor: {1}”, id, dtorCount);
      new Weird(id + 1);
      }

      static void Main()
      {
      new Weird(0);
      }
      }

      On the first run (in a new script window), I get:

      Id: 0, Ctor Invocation #: 1
      Id: 0, Dtor Invocation #: 1
      Id: 1, Ctor Invocation #: 2
      Id: 1, Dtor Invocation #: 2
      Id: 2, Ctor Invocation #: 3
      Id: 2, Dtor Invocation #: 3
      Id: 3, Ctor Invocation #: 4
      Id: 3, Dtor Invocation #: 4
      Id: 4, Ctor Invocation #: 5

      Notice that the destructor for Id 4 is never called. Things get a
      little more interesting on the second run, where I sometimes get:

      Id: 0, Ctor Invocation #: 6
      Id: 4, Dtor Invocation #: 5 <– object from the previous run!
      Id: 5, Ctor Invocation #: 7
      Id: 0, Dtor Invocation #: 6
      Id: 1, Ctor Invocation #: 8
      Id: 1, Dtor Invocation #: 7
      Id: 2, Ctor Invocation #: 9
      Id: 5, Dtor Invocation #: 8
      Id: 6, Ctor Invocation #: 10
      Id: 6, Dtor Invocation #: 9
      Id: 7, Ctor Invocation #: 11
      Id: 2, Dtor Invocation #: 10
      Id: 3, Ctor Invocation #: 12
      Id: 3, Dtor Invocation #: 11
      Id: 4, Ctor Invocation #: 13
      Id: 7, Dtor Invocation #: 12
      Id: 8, Ctor Invocation #: 14

      * Seems to depend on how quickly it is run.

      • In Linqpad, you can use Shift+F5 to reload the query process which will discard the static state and give you deterministic behavior

  2. Wow – it’s been a long time since I visited Chris Brumme’s blog – I was a regular reader way back when. How fortunate we all are that those blog posts are still accessible!

  3. Another myth: Objects to which a finalizable object hold references might not exist when the finalizer runs. Some MS documentation rather unhelpfully (and incorrectly) says that a finalizable object can’t be certain that objects to which it holds references won’t be “collected” before the finalizer runs; correct terminology would say that such objects may have had their Finalize methods run, but the objects themselves will still exist; further, while the GC may have called FInalize, and Finalize may have changed the contents of the objects’ fields, the GC itself will not have done so.

    Note that the myth does contain a somewhat annoying aspect of truth, though: if a finalizable object holds the only strong reference to a WeakReference, there is no guarantee that the WeakReference will be valid when the finalizer runs, even if its target is still valid. The problem is that when a WeakReference becomes eligible for finalization, it will invalidate itself even if its target still exists, and even while references to it may still exist. It would be helpful if the system could supply a `LongWeakReference` type which would hold a string reference to a weak-reference object which held a long-weak-reference GC handles to the target of the LongWeakReference as well as the `LongWeakReference` itself; the “Finalize” method of the “Cleaner” would invalidate both handles if either died; otherwise it would re-register itself for finalization. Unfortunately, I don’t know any way to implement a proper LongWeakReference without either having it maintain a *static* linked list of WeakReference wrapper objects, or else using GC handles directly.

  4. I think omitting deterministic destructors is one of the biggest design flaws of the CLR. Why was that done?

    • There isn’t really much you can do. C++ has no problem because everything is explicitly managed. In C#, you have a GC. Reference counting gives stronger guarantees but can be slower.

    • The design of .NET seems to have borrowed a lot of ideas from Java–some good, some bad. The use of finalization as the primary means of resource cleanup should have been recognizable as a silly idea, but it wasn’t. IMHO, the proper pattern should have been for `Object` to include a protected virtual multi-purpose `ManageLifetime` method which would be called between the time the outermost constructor gives up control and the time execution returns to the caller, and could be called (with different parameters) at various other times as well. Such a method could be called deterministically when a constructor throws an exception and chained to (with hard-coded parameters) from a non-virtual `Object.Dispose` or `Object.MayNeedCleanup`.

      • The funny thing is that C++ CLR allows the use of a syntax similar to stack allocation. When an object goes out of scope Dispose gets called. That’s pretty much as close to real destructors as you need.

        C# could achieve the same by just adding a new keyword like “autodispose” or something like that. Especially if this would cause Dispose calls to child objects in a recursive manner.

      • struct RIAA where T:IDisposable
        {
        public readonly T Value;
        public RIAA(T value)
        {
        Value = value;
        }
        }
        And some magic such that, when a local variable of type RIAA goes out of scope it calls Value.Dispose().

        But to be honest, I don’t think it is so much better than using.

        • using is a very ugly especially if you have many variables. Even the name shouldn’t be “using” but something like “autodispose”.

          • The “using” feature was added late in the ship cycle, and “using” was already a keyword of the language. Had the feature been considered earlier on it seems likely that a different syntax might have been chosen.

  5. I’ve always had mixed feelings about the use of both the ~ClassName() syntax and the term “destructor” in C#. I can appreciate the attempt to use familiar ideas, but C# finalizers and C++ destructors are completely different concepts used for completely different things. I realize the boat has sailed, but I think it would have been better to stick to “finalizer” and to come up with some new syntax, either simply “Finalizer()” or something similar-but-different, like “&ClassName”.

    I’d also be interested to know the answer to adasdasas’s (type that five times fast, ha) question.

    • The best thing C# could have done with finalizers would have been to ignore their existence, and have the compiler treat an override of “Finalize” the same as an override of any other method. The intention of `~` syntax might have been to allow platform-independent cleanup, but since code which declares a destructor will almost always need to include calls to “GC.KeepAlive(Object)” and “GC.SuppressFinalize(Object)”, having such code specify `Object.Finalize()` by name wouldn’t impair portability any more than those other method calls.

  6. In modern C# you should *typically* never find yourself writing a finalizer. Finalizers were previously used for managing unmanaged resources (file handles etc.), however, that mess has been cleaned up with SafeHandle (http://blogs.msdn.com/b/bclteam/archive/2006/06/23/644343.aspx). Therefore finalizers are now basically unnecessary and are an artifact of the .Net 1.0 days.

    You should still implement the disposer pattern ( Dispose(bool) ), but leave off the finalizer. If you are wrapping an unmanaged resource use a SafeHandle because it has a great many more guarantees that a simple finalizer does not.

    • Right, I’ll discuss some of these points in part two. I note however that there are reasons to implement finalizers other than “I need to dispose of this handle”.

    • I agree that finalizers are rarely needed. But there should be an elegant way to to trigger Dispose when something goes out of scope.

  7. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1863

  8. I never liked the “someone pulls the power cord” argument. While it is possible, I think no one should ever make assumptions based on events like this; in fact, we couldn’t even solve the FizzBuzz test correctly if we’d expect our program to terminate abruptly at any time.
    It is true that there are some really critical systems that need to handle such errors, but I’m quite sure that they are solved at a whole different level than garbage collection, so it is meaningless to bring it while talking about that.
    /rant

    Back to finalizers, I think that in .NET — instead of juggling destructors, finalizers and disposables — one should separate object lifetime from resource allocation/deallocation in critical cases. Allocate resources as dictated by the flow of the whole program, not the GC; there are no surprises that way. For non-critical code the GC does a good job anyways.

  9. This was a great overview over various reasons which might prevent a finalizer from being run. Especially the last example was pretty interesting. Looking forward to the next part of this series!

  10. This is an interesting discussion. Every scenario you paint above where finalizers do not run (or don’t run to completion) is true.

    And yet, the C# spec (Section 3.2, last paragraph) states:

    “Prior to an application’s termination, detractors for all of its objects that have not yet been garbage collected are called, …”

    This caused quite a debate when updating the language standard. The guarantee is weak in practice, as you point out above, but a compliant implementation really should try to execute finalizes before a program exits. That’s hard to articulate clearly in the specification.

    • How about throwing OutOfMemory or StackOverflow exceptions when application is terminating and Finalizers keep creating more objects?
      I think the important part of the Spec is that well written finalizers should be executed.

      • Good heavens no. An exception should correctly describe the exceptional circumstance; if the exceptional circumstance is not running out of heap or stack then don’t throw an exception that says that it is. That just makes it harder to diagnose the problem.

        Now, why not throw a special “I couldn’t run all your finalizers” exception? Well, what good would that do? What code would you run in that case? The best thing you could possibly do is log the exception. Maybe to a file. Which requires creating a finalizable object… and now we’re back in the same situation we were in before. Suppose *that* object cannot be finalized, then what do you do? Throw the exception again?

        If the finalizers aren’t being well behaved, the right thing to do is simply to stop running user code, because user code has demonstrated that it is badly behaved.

  11. My favourite myth around finalizers is that they won’t be executed while an instance method of the relevant object is executing. As long as the CLR recognizes that it definitely won’t need to access any *fields* in the object again, it’s happy enough to put the reference on the finalizable queue.

    The C# spec is pretty loose on this front – it talks about an object being “in use” but doesn’t go into details about what that means.

  12. Pingback: When everything you know is wrong, part two | Fabulous adventures in coding

  13. Tut tut. A whole post about .NET’s GC without ever mentioning it’s generational operation – which makes your comments moot.

  14. Pingback: ReSharper Ultimate Bug Fix - The Daily Six Pack

  15. As much as I enjoy reading this, I hate it every time I have to use an IE on this web site, for all code blocks are rendered in a weird non-monospace font (an IE bug/feature existing in all versions up to 11, see e.g. http://stackoverflow.com/q/16812354/11683).

    Would you mind changing your ‘”Courier 10 Pitch”, Courier, monospace;’ to ‘”Courier 10 Pitch”, “Courier New”, monospace;’ please?

  16. Pingback: Automate the Planet

  17. Pingback: Cheatsheet: 2015 05.01 ~ 05.31 | 一世浮华一场空

  18. Pingback: Cheatsheet: 2015 05.01 ~ 05.31 - 编程技术分享

  19. Pingback: Cheatsheet: 2015 05.01 ~ 05.31 - .net开发技术分享

  20. Pingback: Automating object pooling using IDisposable and finalizers | GameDev<T>

  21. Pingback: Allocations and the Garbage Collector – scribdbook

  22. Pingback: Destructors and why you should avoid them | vhsven

  23. Pingback: Sharp Regrets: Top 10 Worst C# Features – Sup

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s