Dispose Tracker
The dispose tracker is a powerful tool for tracking memory problems pertaining to disposable types. Any type that implements IDisposable signals that all its instances should be disposed as soon as they are not needed anymore. Often these types wrap native resources, such as file handles, database connections, bitmaps, etc. The types may also be disposable because they contain references to other disposable instances, or they simply need to know when they are not needed anymore.
By using finalizers, disposable classes wrapping native resources can usually handle the clean-up of its resources. One problem is that there is no guarantee when the finalizer will be called, it may very well take a long time. For scarce resources such as file handles and database connections this can prove to be a very significant problem. There is also a cost involved with using finalizers.
Another problem is that the garbage collector has no notion of the cost of a native resource. For instance, a bitmap may use several megabytes of memory, but the garbage collector only sees the memory used by the instance keeping a handle to the bitmap.
Additionally, the documentation suggests that, if Dispose is not called properly, the finalizer will eventually clean up any native resources and the instance will be garbage collected. However, this is not always the case. Instances of some classes in the framework will never be finalized and garbage collected unless they are disposed.
The abovementioned problems suggest that you should always call Dispose on instances of disposable types, and the dispose tracker helps you to make sure that all instances are disposed.
The dispose tracker is enabled by default unless low-impact profiling is active. The dispose tracker does not use any additional memory, and it has very low performance overhead, so it is recommended that is always be enabled.
To view the information collected by the dispose tracker, the Dispose info field set should be used. When this field set is selected, some of the information presented in the different profiler views is replaced by dispose information. For more information about which information is replaced, see the documentation about Field Sets.
The dispose information includes:
Disposed instances
The numbers presented for disposed instances represent the number of instances that have been disposed, but not yet garbage collected. For more information, see Disposed Instances.Undisposed instances
The numbers presented for undisposed instances represent the number of instances that have been garbage collected without being properly disposed. For more information, see Undisposed instances.
Disposed Instances
Usually, the number of disposed instances for any type should be zero or close to zero. If it is not zero, it may indicate a memory leak. However, there are several possible causes for disposed instances to exist:
There are still short-lived references to the instance, e.g. it may still be referenced by a local variable or an argument. In this case, when collecting another snapshot, the instance should have been garbage collected.
The instance is part of a pool. Disposing the instance simply returns it to the pool to allow it to be reused. In this case it is normal for the instance not to be garbage collected, and it is no indication of a memory leak.
Something else references the instance. This is a very likely memory leak, since usually the instance should no longer be used after it has been disposed. Use the root paths under instance details to try to find out why the instance has not been garbage collected.
An instance is usually considered to be disposed as soon as the Dispose()
method is called, but it is possible for a disposable class to hide the Dispose method and perform the disposal of the instance using a method with a more suitable name instead. For instance, several classes prefer the name Close()
. According to the Dispose pattern guideline, the method replacing Dispose should just call Dispose. Unfortunately, there is a possibility that the hidden Dispose method calls the replacing method. For instance:`
public class ResourceWrapper : IDisposable
{
public virtual void Close()
{
// Release native resources.
//
GC.SuppressFinalize( this );
}
void IDisposable.Dispose()
{
Close();
}
}
In this case, there is a risk that the disposal of the instance is missed and that the instance will be considered �undisposed� when collected.
An instance is considered to be disposed if:
The
Dispose()
method,orIDisposable.Dispose()
if the method is hidden, of the instance is called, before any call toFinalize()
.The
Dispose(bool)
method of the instance is called (if it exists), withtrue
as argument, before any call toFinalize()
.
Undisposed instances
If all instances of a class are properly disposed, the number of undisposed instances will be zero. As mentioned previously, it is preferable to make sure that all instances are properly disposed, but there is a set of disposable classes whose instances do not need to be disposed. These classes do not contain native resources, do not reference other disposable classes, do not need to be cleaned up, and do not have a finalizer. Usually these classes should not be disposable in the first place, but they might be derived from a base class that expects its derived classes to possibly contain native resources, or they might be used as base classes for other classes that may contain native resources.
An example of a disposable class that does not need to be disposed is the System.IO.MemoryStream
class, since it only references managed memory and has no finalizer. It is derived from System.IO.Stream
, an abstract class that can expect classes derived from it to contain native resources, e.g., file handles and network sockets. It therefore implements IDisposable
, even though not all derived classes will contain native resources, e.g., System.IO.MemoryStream
.
Even though there are disposable classes whose instances do not need to be disposed, it is recommended that all instances of all disposable classes always be disposed, unless you are certain that an instance does not need to be disposed.
Note
There are several disposable classes in the framework whose instances are not always disposed. You should not try to dispose these instances yourself, even if you have a reference to the instance. For example, instances of PaintEventArgs
are not always disposed, and it would be possible to dispose them at the end of the OnPaint
method, but that might have undesired consequences. You must only dispose an instance if you clearly know that you are the one responsible for disposing it, and that it is not being used elsewhere.