Обнаружение "просочившихся" IDisposable объектов

Есть много вопросов, которые спрашивают, как обнаружить утечку IDisposable. Кажется, что ответ "вы не можете" .

Я только что проверил с самым тривиальным тестовым случаем, что FxCop 10.0 этого не делает, ReSharper 4 с MSVS2010 этого не делает.

Это кажется мне неправильным, хуже утечек памяти в C (для которых по крайней мере мы установили инструменты для обнаружения).

Я думал: возможно ли, используя рефлексию и другие неясные передовые методы, я могу ввести проверку во время выполнения, в финализаторе, чтобы узнать, был ли вызван Dispose?

Как насчет волшебных трюков с помощью WinDBG + SOS?

Даже если для этого нет существующих инструментов, я хотел бы знать, возможно ли это теоретически (мой С# не очень острый).

Идеи?

ПРИМЕЧАНИЕ Этот заголовок вопроса, возможно, вводит в заблуждение. Реальный вопрос здесь должен состоять в том, был ли объект IDisposable правильным Disposed(). Устранение GC не считается, поскольку я считаю это ошибкой.

Изменить: Решение:.NET Memory Profiler выполняет эту работу. Нам просто нужно спамить несколько GC.Collect() в конце программы, чтобы наш профилировщик мог правильно подобрать статистику.

Ответ 1

Ты не искал достаточно сильно. Существует множество профилей .NET Memory Profilers, которые будут смотреть на вашу программу по мере ее запуска и сообщать вам, где и как используется ваша память (и что ее утечка).

Я бы заметил следующее:

Microsoft CLR Memory Profiler (бесплатно)
RedGate ANTS Memory Profiler
JetBrain DotTrace (включая профайлер кода)
SciTech.NET Memory Profiler

Обновление

SciTech.NET Memory Profiler имеет функцию "Dispose Tracker", которая соответствует счету для запроса OP для отслеживания только вызовов Dispose в их приложении.

Ответ 2

вы можете это сделать, добавив Finalizer к своим IDisposable объектам. В финализаторе вы можете проверить, был ли объект удален или нет. Если он не был удален, вы можете утверждать это или записывать что-то в журнал или что-то еще.

 ~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 - например, который можно использовать в качестве шаблона для реализации Одноразовые Например, шаблон.

Подобно этому, например:

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

Ответ 3

В то время как рекомендация @Justin Niessner работает, я считаю, что использование полномасштабного профилировщика слишком тяжелое.

Я создал свое домашнее решение brew: EyeDisposable. Это приборы для обнаружения, когда Dispose не были вызваны.