Обнаружение «утекших» идентифицируемых объектов - PullRequest
11 голосов
/ 19 января 2011

Есть много вопросов, ТАК спрашивающих, как обнаружить утечку IDisposable объектов. Похоже, что ответ «Вы не можете» .

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

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

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

Как насчет фокусов с WinDBG + SOS?

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

Идеи

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

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

Ответы [ 3 ]

12 голосов
/ 19 января 2011

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

Я бы проверил любое из следующего:

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

Обновление

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

3 голосов
/ 19 января 2011

вы можете сделать это, добавив финализатор к вашим 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,например, можно использовать в качестве шаблона для реализации шаблона 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 голосов
/ 22 мая 2011

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

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

...