检测“泄露”的IDisposable对象

时间:2011-01-19 15:25:34

标签: c# .net idisposable memory-leaks

有很多问题要求如何检测IDisposable对象泄漏。似乎答案是"you can't"

我刚用最琐碎的测试案例检查过,FxCop 10.0没有这样做,带有MSVS2010的ReSharper 4没有这样做。

这对我来说似乎不对,比C中的内存泄漏更糟糕(至少我们已经建立了检测工具)。

我在想:是否有可能使用反射和其他模糊的高级技术,我可以在运行时注入一个检查,在终结器中查看是否已调用Dispose

WinDBG + SOS的魔术技巧怎么样?

即使没有现成的工具,我也想知道这在理论上是否可行(我的C#不是很尖锐)。

想法?

注意此问题的标题可能会产生误导。这里真正的问题应该是 IDisposable对象是否Disposed()正确。由于我认为这是一个错误,因此由GC处理并不重要。

编辑:解决方案:.NET Memory Profiler完成了这项工作。我们只需要在程序结束时发送几个GC.Collect()垃圾邮件,以便我们的探查器能够正确获取统计信息。

3 个答案:

答案 0 :(得分:12)

你搜索得不够好。有很多.NET内存配置文件可以在程序运行时查看你的程序,并让你知道你的内存使用的位置和方式(以及泄漏的内容)。

我会查看以下任何内容:

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

<强>更新

SciTech的.NET内存分析器有一个名为“Dispose Tracker”的功能,符合OP要求在其应用程序中仅跟踪Dispose调用的请求。

答案 1 :(得分:3)

你可以通过向IDisposable对象添加Finalizer来实现。 在终结器中,您可以检查对象是否已被丢弃。如果尚未处理,您可以断言,或者将某些内容写入日志或其他任何内容。

 ~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);
 }

您可以将此功能纳入基类 - Disposable - 例如,可以将其用作模板来实现Disposable模式。

像这样,例如:

    /// <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);
        }
    }

答案 2 :(得分:2)

虽然@Justin Niessner的推荐有效,但我发现使用完整的剖析器太重了。

我创建了我的家酿解决方案:EyeDisposable。它检测程序集以检测何时未调用Dispose