Wednesday, 23 August 2006

You must call Dispose

You must call Dispose.

If an object implements the IDisposable pattern, or otherwise offers a Dispose or Close method, you must call it. No exceptions. If an instance member of your class implements IDisposable, your class should too.

OK, if you don’t, a finalizer might clean up after you. But you don’t want it to do that. The finalizer will only run once a GC has occurred. And a GC only runs based on heuristics of how much memory has been allocated (in general; there are a few cases in Compact Framework where the GC can be spurred to run, for example on receiving a WM_HIBERNATE message from the shell to say that physical memory is low). In .NET Framework 1.x and both versions of .NET Compact Framework, the GC has no idea how much unmanaged memory is being used by any object that manages an unmanaged resource. .NET Framework 2.0 does have the GC.AddMemoryPressure method, which can guide the GC to collect earlier than it might otherwise have done.

Finalizers don’t run on a regular application thread. They run on a special finalizer thread. This means you have to be careful around possible synchronisation issues. Objects to be finalized wait in a queue, and only one thread services that queue, so if a finalizer blocks, all undisposed objects will end up hanging around.

Once the finalizer has run, the managed memory isn’t automatically released. You have to wait for GC to run again. On the desktop or server, with the full Framework, you have to wait for it to collect the generation of the heap which the object is now in, which means an even longer wait for the managed memory to be released, which can keep the GC heap larger than it could have been.

The GC propoganda basically tells us we can be lazy. We can’t. We must clean up after ourselves. Treat the finalizer as a safety net.

Best practice is to manage one unmanaged resource with one managed object, and keep that managed object as simple as possible – ideally, just to manage that object. Make your resource manager class implement IDisposable and give it a finalizer as a backstop.

4 comments:

Richard said...

A good practice to get into when working in C#, and VB.Net 2005 is to use the 'Using' structure which ensures that the dispose method is always called.

Another area where making sure things get disposed is important is when working with COM objects, especially when like a number of exisiting systems I have come across you have COM objects that have to be released at exact moments.

Anonymous said...

The big pain with this is that lots of classes end up implementing IDisposable when they don't need to because of a design flaw in the .NET Framework Class Libraries.

The flaw is that IComponent derives from IDisposable.

Why's that a problem? Well suppose I've written a class that I *know* has absolutely no need to be disposed. (E.g. its only fields are a couple of strings, say.) But suppose I want to implement IComponent - I am now *forced* to implement IDisposable. In order to plug into a designer environment I am required to supply an empty Dispose method, and I've now essentially told my callers "you must Dispose me" even though there's absolutely no reason for them to Dispose me.

This is really unhelpful, because it means there are now loads of classes with pointless Dispose methods. DataSet is the poster child for this problem. It doesn't do anything in its Dispose method. The only reason it has a Dispose method is because it implements IComponent.

IComponent should never have been made to derive from IDisposable.

I grudgingly Dispose my DataSets these days, because I tell other people just what you're telling them: Dispose is important. But I resent doing it slightly because I know it does nothing in this particular case. I tell myself "Well, maybe in .NET 6.1 they'll put something important in DataSet.Dispose" in an attempt to convince myself that it's not a total waste of time. But secretly, I know that'll never happen because there's so much code out there that doesn't Dispose its DataSets that there's no way Microsoft could ever change it so that Disposing a DataSet was actually important.

Anonymous said...

In N-tier app, DataAccess need to implement IDisposable to dispose command, database, etc... objects when using DataAccess Application Block. Is it required to implement IDisposable in all previous layers to dispose these objects?

Anonymous said...

There are cases where you may not know when the object is no longer being used and therefore cannot call Dispose. Especially for objects like datasets and datatables that are passed around even between tiers.