c# - Proper use of the IDisposable interface

ID : 560

viewed : 252

Tags : c#.netgarbage-collectionidisposablec#

Top 5 Answer for c# - Proper use of the IDisposable interface

vote vote

93

The point of Dispose is to free unmanaged resources. It needs to be done at some point, otherwise they will never be cleaned up. The garbage collector doesn't know how to call DeleteHandle() on a variable of type IntPtr, it doesn't know whether or not it needs to call DeleteHandle().

Note: What is an unmanaged resource? If you found it in the Microsoft .NET Framework: it's managed. If you went poking around MSDN yourself, it's unmanaged. Anything you've used P/Invoke calls to get outside of the nice comfy world of everything available to you in the .NET Framework is unmanaged – and you're now responsible for cleaning it up.

The object that you've created needs to expose some method, that the outside world can call, in order to clean up unmanaged resources. The method can be named whatever you like:

public void Cleanup() 

or

public void Shutdown() 

But instead there is a standardized name for this method:

public void Dispose() 

There was even an interface created, IDisposable, that has just that one method:

public interface IDisposable {    void Dispose() } 

So you make your object expose the IDisposable interface, and that way you promise that you've written that single method to clean up your unmanaged resources:

public void Dispose() {    Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); } 

And you're done. Except you can do better.


What if your object has allocated a 250MB System.Drawing.Bitmap (i.e. the .NET managed Bitmap class) as some sort of frame buffer? Sure, this is a managed .NET object, and the garbage collector will free it. But do you really want to leave 250MB of memory just sitting there – waiting for the garbage collector to eventually come along and free it? What if there's an open database connection? Surely we don't want that connection sitting open, waiting for the GC to finalize the object.

If the user has called Dispose() (meaning they no longer plan to use the object) why not get rid of those wasteful bitmaps and database connections?

So now we will:

  • get rid of unmanaged resources (because we have to), and
  • get rid of managed resources (because we want to be helpful)

So let's update our Dispose() method to get rid of those managed objects:

public void Dispose() {    //Free unmanaged resources    Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);     //Free managed resources too    if (this.databaseConnection != null)    {       this.databaseConnection.Dispose();       this.databaseConnection = null;    }    if (this.frameBufferImage != null)    {       this.frameBufferImage.Dispose();       this.frameBufferImage = null;    } } 

And all is good, except you can do better!


What if the person forgot to call Dispose() on your object? Then they would leak some unmanaged resources!

Note: They won't leak managed resources, because eventually the garbage collector is going to run, on a background thread, and free the memory associated with any unused objects. This will include your object, and any managed objects you use (e.g. the Bitmap and the DbConnection).

If the person forgot to call Dispose(), we can still save their bacon! We still have a way to call it for them: when the garbage collector finally gets around to freeing (i.e. finalizing) our object.

Note: The garbage collector will eventually free all managed objects. When it does, it calls the Finalize method on the object. The GC doesn't know, or care, about your Dispose method. That was just a name we chose for a method we call when we want to get rid of unmanaged stuff.

The destruction of our object by the Garbage collector is the perfect time to free those pesky unmanaged resources. We do this by overriding the Finalize() method.

Note: In C#, you don't explicitly override the Finalize() method. You write a method that looks like a C++ destructor, and the compiler takes that to be your implementation of the Finalize() method:

~MyObject() {     //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to     Dispose(); //<--Warning: subtle bug! Keep reading! } 

But there's a bug in that code. You see, the garbage collector runs on a background thread; you don't know the order in which two objects are destroyed. It is entirely possible that in your Dispose() code, the managed object you're trying to get rid of (because you wanted to be helpful) is no longer there:

public void Dispose() {    //Free unmanaged resources    Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);     //Free managed resources too    if (this.databaseConnection != null)    {       this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it       this.databaseConnection = null;    }    if (this.frameBufferImage != null)    {       this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it       this.frameBufferImage = null;    } } 

So what you need is a way for Finalize() to tell Dispose() that it should not touch any managed resources (because they might not be there anymore), while still freeing unmanaged resources.

The standard pattern to do this is to have Finalize() and Dispose() both call a third(!) method; where you pass a Boolean saying if you're calling it from Dispose() (as opposed to Finalize()), meaning it's safe to free managed resources.

This internal method could be given some arbitrary name like "CoreDispose", or "MyInternalDispose", but is tradition to call it Dispose(Boolean):

protected void Dispose(Boolean disposing) 

But a more helpful parameter name might be:

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects) {    //Free unmanaged resources    Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);     //Free managed resources too, but only if I'm being called from Dispose    //(If I'm being called from Finalize then the objects might not exist    //anymore    if (itIsSafeToAlsoFreeManagedObjects)      {           if (this.databaseConnection != null)       {          this.databaseConnection.Dispose();          this.databaseConnection = null;       }       if (this.frameBufferImage != null)       {          this.frameBufferImage.Dispose();          this.frameBufferImage = null;       }    } } 

And you change your implementation of the IDisposable.Dispose() method to:

public void Dispose() {    Dispose(true); //I am calling you from Dispose, it's safe } 

and your finalizer to:

~MyObject() {    Dispose(false); //I am *not* calling you from Dispose, it's *not* safe } 

Note: If your object descends from an object that implements Dispose, then don't forget to call their base Dispose method when you override Dispose:

public override void Dispose() {     try     {         Dispose(true); //true: safe to free managed resources     }     finally     {         base.Dispose();     } } 

And all is good, except you can do better!


If the user calls Dispose() on your object, then everything has been cleaned up. Later on, when the garbage collector comes along and calls Finalize, it will then call Dispose again.

Not only is this wasteful, but if your object has junk references to objects you already disposed of from the last call to Dispose(), you'll try to dispose them again!

You'll notice in my code I was careful to remove references to objects that I've disposed, so I don't try to call Dispose on a junk object reference. But that didn't stop a subtle bug from creeping in.

When the user calls Dispose(): the handle CursorFileBitmapIconServiceHandle is destroyed. Later when the garbage collector runs, it will try to destroy the same handle again.

protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize) {    //Free unmanaged resources    Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy     ... } 

The way you fix this is tell the garbage collector that it doesn't need to bother finalizing the object – its resources have already been cleaned up, and no more work is needed. You do this by calling GC.SuppressFinalize() in the Dispose() method:

public void Dispose() {    Dispose(true); //I am calling you from Dispose, it's safe    GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later } 

Now that the user has called Dispose(), we have:

  • freed unmanaged resources
  • freed managed resources

There's no point in the GC running the finalizer – everything's taken care of.

Couldn't I use Finalize to cleanup unmanaged resources?

The documentation for Object.Finalize says:

The Finalize method is used to perform cleanup operations on unmanaged resources held by the current object before the object is destroyed.

But the MSDN documentation also says, for IDisposable.Dispose:

Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

So which is it? Which one is the place for me to cleanup unmanaged resources? The answer is:

It's your choice! But choose Dispose.

You certainly could place your unmanaged cleanup in the finalizer:

~MyObject() {    //Free unmanaged resources    Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);     //A C# destructor automatically calls the destructor of its base class. } 

The problem with that is you have no idea when the garbage collector will get around to finalizing your object. Your un-managed, un-needed, un-used native resources will stick around until the garbage collector eventually runs. Then it will call your finalizer method; cleaning up unmanaged resources. The documentation of Object.Finalize points this out:

The exact time when the finalizer executes is undefined. To ensure deterministic release of resources for instances of your class, implement a Close method or provide a IDisposable.Dispose implementation.

This is the virtue of using Dispose to cleanup unmanaged resources; you get to know, and control, when unmanaged resource are cleaned up. Their destruction is "deterministic".


To answer your original question: Why not release memory now, rather than for when the GC decides to do it? I have a facial recognition software that needs to get rid of 530 MB of internal images now, since they're no longer needed. When we don't: the machine grinds to a swapping halt.

Bonus Reading

For anyone who likes the style of this answer (explaining the why, so the how becomes obvious), I suggest you read Chapter One of Don Box's Essential COM:

In 35 pages he explains the problems of using binary objects, and invents COM before your eyes. Once you realize the why of COM, the remaining 300 pages are obvious, and just detail Microsoft's implementation.

I think every programmer who has ever dealt with objects or COM should, at the very least, read the first chapter. It is the best explanation of anything ever.

Extra Bonus Reading

When everything you know is wrong archiveby Eric Lippert

It is therefore very difficult indeed to write a correct finalizer, and the best advice I can give you is to not try.

vote vote

88

IDisposable is often used to exploit the using statement and take advantage of an easy way to do deterministic cleanup of managed objects.

public class LoggingContext : IDisposable {     public Finicky(string name) {         Log.Write("Entering Log Context {0}", name);         Log.Indent();     }     public void Dispose() {         Log.Outdent();     }      public static void Main() {         Log.Write("Some initial stuff.");         try {             using(new LoggingContext()) {                 Log.Write("Some stuff inside the context.");                 throw new Exception();             }         } catch {             Log.Write("Man, that was a heavy exception caught from inside a child logging context!");         } finally {             Log.Write("Some final stuff.");         }     } } 
vote vote

71

The purpose of the Dispose pattern is to provide a mechanism to clean up both managed and unmanaged resources and when that occurs depends on how the Dispose method is being called. In your example, the use of Dispose is not actually doing anything related to dispose, since clearing a list has no impact on that collection being disposed. Likewise, the calls to set the variables to null also have no impact on the GC.

You can take a look at this article for more details on how to implement the Dispose pattern, but it basically looks like this:

public class SimpleCleanup : IDisposable {     // some fields that require cleanup     private SafeHandle handle;     private bool disposed = false; // to detect redundant calls      public SimpleCleanup()     {         this.handle = /*...*/;     }      protected virtual void Dispose(bool disposing)     {         if (!disposed)         {             if (disposing)             {                 // Dispose managed resources.                 if (handle != null)                 {                     handle.Dispose();                 }             }              // Dispose unmanaged managed resources.              disposed = true;         }     }      public void Dispose()     {         Dispose(true);         GC.SuppressFinalize(this);     } } 

The method that is the most important here is the Dispose(bool), which actually runs under two different circumstances:

  • disposing == true: the method has been called directly or indirectly by a user's code. Managed and unmanaged resources can be disposed.
  • disposing == false: the method has been called by the runtime from inside the finalizer, and you should not reference other objects. Only unmanaged resources can be disposed.

The problem with simply letting the GC take care of doing the cleanup is that you have no real control over when the GC will run a collection cycle (you can call GC.Collect(), but you really shouldn't) so resources may stay around longer than needed. Remember, calling Dispose() doesn't actually cause a collection cycle or in any way cause the GC to collect/free the object; it simply provides the means to more deterministicly cleanup the resources used and tell the GC that this cleanup has already been performed.

The whole point of IDisposable and the dispose pattern isn't about immediately freeing memory. The only time a call to Dispose will actually even have a chance of immediately freeing memory is when it is handling the disposing == false scenario and manipulating unmanaged resources. For managed code, the memory won't actually be reclaimed until the GC runs a collection cycle, which you really have no control over (other than calling GC.Collect(), which I've already mentioned is not a good idea).

Your scenario isn't really valid since strings in .NET don't use any unamanged resources and don't implement IDisposable, there is no way to force them to be "cleaned up."

vote vote

60

There should be no further calls to an object's methods after Dispose has been called on it (although an object should tolerate further calls to Dispose). Therefore the example in the question is silly. If Dispose is called, then the object itself can be discarded. So the user should just discard all references to that whole object (set them to null) and all the related objects internal to it will automatically get cleaned up.

As for the general question about managed/unmanaged and the discussion in other answers, I think any answer to this question has to start with a definition of an unmanaged resource.

What it boils down to is that there is a function you can call to put the system into a state, and there's another function you can call to bring it back out of that state. Now, in the typical example, the first one might be a function that returns a file handle, and the second one might be a call to CloseHandle.

But - and this is the key - they could be any matching pair of functions. One builds up a state, the other tears it down. If the state has been built but not torn down yet, then an instance of the resource exists. You have to arrange for the teardown to happen at the right time - the resource is not managed by the CLR. The only automatically managed resource type is memory. There are two kinds: the GC, and the stack. Value types are managed by the stack (or by hitching a ride inside reference types), and reference types are managed by the GC.

These functions may cause state changes that can be freely interleaved, or may need to be perfectly nested. The state changes may be threadsafe, or they might not.

Look at the example in Justice's question. Changes to the Log file's indentation must be perfectly nested, or it all goes wrong. Also they are unlikely to be threadsafe.

It is possible to hitch a ride with the garbage collector to get your unmanaged resources cleaned up. But only if the state change functions are threadsafe and two states can have lifetimes that overlap in any way. So Justice's example of a resource must NOT have a finalizer! It just wouldn't help anyone.

For those kinds of resources, you can just implement IDisposable, without a finalizer. The finalizer is absolutely optional - it has to be. This is glossed over or not even mentioned in many books.

You then have to use the using statement to have any chance of ensuring that Dispose is called. This is essentially like hitching a ride with the stack (so as finalizer is to the GC, using is to the stack).

The missing part is that you have to manually write Dispose and make it call onto your fields and your base class. C++/CLI programmers don't have to do that. The compiler writes it for them in most cases.

There is an alternative, which I prefer for states that nest perfectly and are not threadsafe (apart from anything else, avoiding IDisposable spares you the problem of having an argument with someone who can't resist adding a finalizer to every class that implements IDisposable).

Instead of writing a class, you write a function. The function accepts a delegate to call back to:

public static void Indented(this Log log, Action action) {     log.Indent();     try     {         action();     }     finally     {         log.Outdent();     } } 

And then a simple example would be:

Log.Write("Message at the top"); Log.Indented(() => {     Log.Write("And this is indented");      Log.Indented(() =>     {         Log.Write("This is even more indented");     }); }); Log.Write("Back at the outermost level again"); 

The lambda being passed in serves as a code block, so it's like you make your own control structure to serve the same purpose as using, except that you no longer have any danger of the caller abusing it. There's no way they can fail to clean up the resource.

This technique is less useful if the resource is the kind that may have overlapping lifetimes, because then you want to be able to build resource A, then resource B, then kill resource A and then later kill resource B. You can't do that if you've forced the user to perfectly nest like this. But then you need to use IDisposable (but still without a finalizer, unless you have implemented threadsafety, which isn't free).

vote vote

51

Scenarios I make use of IDisposable: clean up unmanaged resources, unsubscribe for events, close connections

The idiom I use for implementing IDisposable (not threadsafe):

class MyClass : IDisposable {     // ...      #region IDisposable Members and Helpers     private bool disposed = false;      public void Dispose() {         Dispose(true);         GC.SuppressFinalize(this);     }      private void Dispose(bool disposing) {         if (!this.disposed) {             if (disposing) {                 // cleanup code goes here             }             disposed = true;         }     }      ~MyClass() {         Dispose(false);     }     #endregion } 

Top 3 video Explaining c# - Proper use of the IDisposable interface

Related QUESTION?