Finalizers and weak references

What do finalizers and weak references have in common? Well more than you might think actually.

 

Finalizers

Finalizers are clean-up code that will be run at the end of an objects life-cycle. You should only release native resources in the finalizer. When you use your object you should not the finalizer cleaning up after you. You're much better off performance-wise by making your object Disposable and making sure that you're actually calling the Dispose method. The finalizers should only be the final fail-safe in case the Dispose call is forgotten.

Having a finalizer means your object is always promoted at least one generation by the Garbage Collector (GC). Here's why:

  • You have an object with a finalizer and the object is no longer referenced
  • The GC comes to collect the object. It sees that the object has a finalizer. The GC therefore adds the object to the finalizer queue and promotes it one generation on the heap
  • The finalizer is run, once the finalizer thread is available. (If this takes too long the object might actually be promoted a second time.)
  • The object is finally garbage collected

Since you only have one finalizer thread and rely heavily on finalizers you may be creating an unintentional bottleneck.

So shouldn't I use finalizers?

Well sure you should. But as I've said before, only regard them as a fail-safe. Make your objects disposable and add a finalizer just in case somebody less skilled than yourself forgets to call the Dispose method. :-)


 

Weak References

A weak reference is different from a strong reference since this reference is disregarded when the GC comes to collect the object, so if there are nothing but weak references remaining for an object when the heap is GC' d the object will be collected. Weak references are generally useful in two ways:

1. Poor mans cache

If you have a weak reference to an object, say a dataset that takes some time putting together, then you can still work with that object as long as it hasn't been garbage collected. You would work with the reference as you would with any cached item. First you check if the object still exists, if it does, then you get a strong reference to the object and work with it. If not, then you create a new dataset and and work with that one.

2. Custom finalizer

Consider the following scenario. (No, you're not having Déjà Vu. This is the same scenario I described in the finalizers section above.):

  • You have an object with a finalizer and the object is no longer referenced
  • The GC comes to collect the object. It sees that the object has a finalizer. The GC therefore adds the object to the finalizer queue and promotes it one generation on the heap
  • The finalizer is run, once the finalizer thread is available. (If this takes too long the object might actually be promoted a second time.)
  • The object is finally garbage collected

Here's an alternative way of doing the same thing:

  • You don't use a finalizer. Instead you have an array of weak references to objects that you occasionally check
  • When you find that one of the weak references' target is null that means the GC has collected that object
  • The object has been garbage collected, so now you run your own clean-up code.

Okay, so the second alternative has two obvious benefits compared to the first:

  1. Your object won't be automatically promoted to generation 1 or generation 2.
  2. You won't be depending on the finalizer thread being available to run your clean-up code.

The ultimate option would off course be to have your object being Disposable, make sure you call the Dispose method and use alternatives one or two as back-ups.


 

Long Weak References

Long weak references will survive the finalizer, so even if your object has been finalized you can still reference it. This may be useful in certain situations, but it can be risky if you're not 100% sure what the finalizer does. Let's say your object performs some kind of file I/O and all file references are closed by the finalizer. If you reference this object using a Long Weak Reference you will have an instance of the object with all I/O functionality disabled. Obviously this could cause all sorts of exceptions if you don't know what you're doing.


 

Myths and Facts

I've previously gotten some positive feedback on the Myth / Fact format, so I thought I'd round this topic off by adding a summary using that format. Here goes:

Myth:
You can pretty much do anything in the finalizer.

Fact:
The only thing you should do in the finalizer is to release your native resources. The reason why your doing this is because you can never be sure that the person using your object is smart enough to call the Dispose method. If everybody did do that, then we wouldn't need the finalizer at all

Myth:
If you have a finalizer you don't need to make your component disposable

Fact:
You should always call the Dispose method of any disposable object. It doesn't matter if all it seems to do is set GC.SuppressFinalize(Me). That is no guarantee that the next version of the object won't do all kinds of stuff in the Dispose method. The rule of thumb is: If it is disposable, dispose it.

Myth:
Having a finalizer has no impact on performance. When the Garbage Collector (GC) comes to collect it the GC will simply call the Finalize method first and then collect it.

Fact:
Having a finalizer is a guarantee that your object will be promoted one generation before it is collected by the GC. When the GC comes across an object with a finalizer that is ready to be collected it puts the object in the finalizer queue, but the object is then moved to the next generation. For more information on this, please see my previous post on the managed heap, the GC and its generations. You also have only one finalizer thread, so this has bottleneck warning written all over it.

Myth:
Long Weak References are available as long as the object is in the finalizer queue, but they're released once the Finalize method is called.

Fact:
Nope. They are available when they're in the finalizer queue, but they're also available after the finalizer has been called. You really should be aware of this or you might get the strangest results.


 

Jedi skills

If you want to know more about finalizers, weak references, the GC and other goodies I highly recommend checking out Maoni's WebLog. What better place to learn about this than from one of the developers?

 

Over and out

/ Johan