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 Framwork
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 can call, in order to clean up unmanaged resources. There is even a standardized name for this method you must create:
public void Dispose()
There was even an interface created, IDisposable
, that has just this one method:
public interface class 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.gdiCursorBitmapStreamFileHandle);
}
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? Do you really want that 250MB of memory just sitting there, waiting for the garbage collector to eventually come along and call Dispose? Or what if you have a database DbConnection open? The user has told us they don't our object anymore, why not get rid of those wasteful Bitmaps and database connections?
So now you get rid of unmanaged resources (because you have to), and you also get rid of managed resources (because you want to be helpful):
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//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 using your object simply forgets to even call Dispose()
when they're done with it? 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.
If the person forgot to call Dispose, we can still save their ass! We still have a way to call it for them: when the garbage collector finally gets around to freeing our object.
Note: The garbage collector will eventually free all managed objects.
When it does it calls the Finalize
method. 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 Garbage collector destroying our object 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:
public ~MyObject()
{
//we're being 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 way for Finalize
to tellDispose
that it should not touch any managed resources (because they might not be there anymore).
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), and so its 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 freeManagedObjectsAlso)
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//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 (freeManagedObjectsAlso)
{
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.Dipose() method to:
public void Dispose()
{
Dispose(true); //i am calling you from Dispose, it's safe
}
and your finalizer to:
public ~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 t call their base Dispose method
when you overrode Dispose:
public 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 gdiCursorBitmapStreamFileHandle 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.gdiCursorBitmapStreamFileHandle); <--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 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
}
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.
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, that they're no longer needed. Otherwise the machine grinds to a swapping halt.