简体   繁体   中英

Detecting “leaked” IDisposable objects

There are many questions SO asking how to detect IDisposable objects leak. It seems like the answer is "you can't" .

I just checked with the most trivial test case, that FxCop 10.0 doesn't do it, ReSharper 4 with MSVS2010 doesn't do it.

This seems wrong to me, worse than memory leaks in C (for which at least we have established tools to detect).

I was thinking: Is it possible, using reflection and other obscure advanced techniques, that I can inject a check at runtime, in the finalizer to see whether Dispose has been called?

How about magic tricks with WinDBG+SOS?

Even if there aren't existing tools to do it, I'd like to know whether this is theoretically possible (my C# isn't very sharp).

Ideas?

NOTE This question's title might have been misleading. The real question here should be whether a IDisposable object has been Disposed() properly . Getting disposed by the GC doesn't count since I regard that as a mistake.

Edit : Solution: .NET Memory Profiler does the job. We just need to spam several GC.Collect() at the end of the program to enable our profiler to correctly pick up the stats.

You didn't search hard enough. There are plenty of .NET Memory Profilers out there that will look at your program as it runs and let you know where/how your memory is used (and what is leaking it).

I would check out any of the following:

Microsoft's CLR Memory Profiler (free)
RedGate ANTS Memory Profiler
JetBrain's DotTrace (includes code profiler as well)
SciTech .NET Memory Profiler

Update

SciTech's .NET Memory Profiler has a feature called 'Dispose Tracker' that fits the bill for the OP's request of tracking only the Dispose calls in their application.

you can do it, by adding a Finalizer to your IDisposable objects. In the finalizer, you can check whether the object has been disposed or not. If it hasn't been disposed, you can assert this, or write something to a log, or whatever.

 ~Disposable()
 {
#if DEBUG
            // In debug-builds, make sure that a warning is displayed when the Disposable object hasn't been
            // disposed by the programmer.

            if( _disposed == false )
            {
                System.Diagnostics.Debug.Fail ("There is a disposable object which hasn't been disposed before the finalizer call: {0}".FormatString (this.GetType ().Name));
            }
#endif
            Dispose (false);
 }

You can factor this functionality into a base class - Disposable - for instance, which can be used as a template to implement the Disposable pattern for instance.

Like this, for instance:

    /// <summary>
    /// Abstract base class for Disposable types.    
    /// </summary>
    /// <remarks>This class makes it easy to correctly implement the Disposable pattern, so if you have a class which should
    /// be IDisposable, you can inherit from this class and implement the DisposeManagedResources and the
    /// DisposeUnmanagedResources (if necessary).
    /// </remarks>
    public abstract class Disposable : IDisposable
    {
        private bool                    _disposed = false;

        /// <summary>
        /// Releases the managed and unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose (true);
            GC.SuppressFinalize (this);
        }

        /// <summary>
        /// Releases the unmanaged and managed resources.
        /// </summary>
        /// <param name="disposing">When disposing is true, the managed and unmanaged resources are
        /// released.
        /// When disposing is false, only the unmanaged resources are released.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
        protected void Dispose( bool disposing )
        {
            // We can suppress the CA1063 Message on this method, since we do not want that this method is 
            // virtual.  
            // Users of this class should override DisposeManagedResources and DisposeUnmanagedResources.
            // By doing so, the Disposable pattern is also implemented correctly.

            if( _disposed == false )
            {
                if( disposing )
                {
                    DisposeManagedResources ();
                }
                DisposeUnmanagedResources ();

                _disposed = true;
            }
        }

        /// <summary>
        /// Override this method and implement functionality to dispose the 
        /// managed resources.
        /// </summary>
        protected abstract void DisposeManagedResources();

        /// <summary>
        /// Override this method if you have to dispose Unmanaged resources.
        /// </summary>
        protected virtual void DisposeUnmanagedResources()
        {
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="Disposable"/> is reclaimed by garbage collection.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
        ~Disposable()
        {
#if DEBUG
            // In debug-builds, make sure that a warning is displayed when the Disposable object hasn't been
            // disposed by the programmer.

            if( _disposed == false )
            {
                System.Diagnostics.Debug.Fail ("There is a disposable object which hasn't been disposed before the finalizer call: {0}".FormatString (this.GetType ().Name));
            }
#endif
            Dispose (false);
        }
    }

While @Justin Niessner's recommendation works, I find that using a full blown profiler too heavy.

I created my home-brew solution: EyeDisposable . It instruments assemblies to detect when Dispose have not been called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM