Main menu

Post navigation

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?

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.

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

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

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.

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().

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.

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.

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.

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.

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.

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.

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).