The GC does not call Dispose , it calls your finalizer (which you should make call Dispose(false) ).
If you don't use using , then it's up to you (the calling code) to dispose of your object by explicitely calling Dispose().
IDisposable is usually used when a class has some expensive or unmanaged resources allocated which need to be released after their usage. Not disposing an object can lead to memory leaks.
Before the GC deallocates the memory, the framework calls the object's Finalize() method, but developers are responsible for calling the Dispose() method. The two methods are not equivalent. Even though both methods perform object clean-up, there are distinct differences between them.
The .Net Garbage Collector calls the Object.Finalize method of an object on garbage collection. By default this does nothing and must be overidden if you want to free additional resources.
Dispose is NOT automatically called and must be explicity called if resources are to be released, such as within a 'using' or 'try finally' block
see http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx for more information
I want to emphasize Brian's point in his comment, because it is important.
Finalizers are not deterministic destructors like in C++. As others have pointed out, there is no guarantee of when it will be called, and indeed if you have enough memory, if it will ever be called.
But the bad thing about finalizers is that, as Brian said, it causes your object to survive a garbage collection. This can be bad. Why?
As you may or may not know, the GC is split into generations - Gen 0, 1 and 2, plus the Large Object Heap. Split is a loose term - you get one block of memory, but there are pointers of where the Gen 0 objects start and end.
The thought process is that you'll likely use lots of objects that will be short lived. So those should be easy and fast for the GC to get to - Gen 0 objects. So when there is memory pressure, the first thing it does is a Gen 0 collection.
Now, if that doesn't resolve enough pressure, then it goes back and does a Gen 1 sweep (redoing Gen 0), and then if still not enough, it does a Gen 2 sweep (redoing Gen 1 and Gen 0). So cleaning up long lived objects can take a while and be rather expensive (since your threads may be suspended during the operation).
This means that if you do something like this:
~MyClass() { }
Your object, no matter what, will live to Generation 2. This is because the GC has no way of calling the finalizer during garbage collection. So objects that have to be finalized are moved to a special queue to be cleaned out by a different thread (the finalizer thread - which if you kill makes all kinds of bad things happen). This means your objects hang around longer, and potentially force more garbage collections.
So, all of that is just to drive home the point that you want to use IDisposable to clean up resources whenever possible and seriously try to find ways around using the finalizer. It's in your application's best interests.
There's lots of good discussion already here, and I'm a little late to the party, but I wanted to add a few points myself.
class SomeObject : IDisposable {
IntPtr _SomeNativeHandle;
FileStream _SomeFileStream;
// Something useful here
~ SomeObject() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
GC.SuppressFinalize(this);
//Because the object was explicitly disposed, there will be no need to
//run the finalizer. Suppressing it reduces pressure on the GC
//The managed reference to an IDisposable is disposed only if the
_SomeFileStream.Dispose();
}
//Regardless, clean up the native handle ourselves. Because it is simple a member
// of the current instance, the GC can't have done anything to it,
// and this is the onlyplace to safely clean up
if(IntPtr.Zero != _SomeNativeHandle) {
NativeMethods.CloseHandle(_SomeNativeHandle);
_SomeNativeHandle = IntPtr.Zero;
}
}
}
That's the simple version, but there are a lot of nuances that can trip you up on this pattern.
In my opinion, it is much better to completely avoid having any types that directly contain both disposable references and native resources that may require finalization. SafeHandles provide a very clean way of doing this by encapsulating native resources into disposable that internally provide their own finalization (along with a number of other benefits like removing the window during P/Invoke where a native handle could be lost due to an asynchronous exception).
Simply defining a SafeHandle makes this Trivial:
private class SomeSafeHandle
: SafeHandleZeroOrMinusOneIsInvalid {
public SomeSafeHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{ return NativeMethods.CloseHandle(handle); }
}
Allows you to simplify the containing type to:
class SomeObject : IDisposable {
SomeSafeHandle _SomeSafeHandle;
FileStream _SomeFileStream;
// Something useful here
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}
I don't think so. You have control over when Dispose is called, which means you could in theory write disposal code that makes assumptions about (for instance) the existence of other objects. You have no control over when the finalizer is called, so it would be iffy to have the finalizer automatically call Dispose on your behalf.
EDIT: I went away and tested, just to make sure:
class Program
{
static void Main(string[] args)
{
Fred f = new Fred();
f = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Fred's gone, and he's not coming back...");
Console.ReadLine();
}
}
class Fred : IDisposable
{
~Fred()
{
Console.WriteLine("Being finalized");
}
void IDisposable.Dispose()
{
Console.WriteLine("Being Disposed");
}
}
Not in the case you describe, But the GC will call the Finalizer for you, if you have one.
HOWEVER. The next garbage collection ,instead of being collected, the object will go into the finalization que, everything gets collected, then it's finalizer called. The next collection after that it will be freed.
Depending on the memory pressure of your app, you may not have a gc for that object generation for a while. So in the case of say, a file stream or a db connection, you may have to wait a while for the unmanaged resource to be freed in the finalizer call for a while, causing some issues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With