Когда я должен использовать GC.SuppressFinalize()?

В.NET, при каких обстоятельствах я должен использовать GC.SuppressFinalize()?

Какие преимущества дает мне этот метод?

5 ответов

Решение

SuppressFinalize должен вызываться только классом, имеющим финализатор. Он сообщает сборщику мусора (GC), что this Объект был полностью очищен.

Рекомендуемый шаблон IDisposable при наличии финализатора:

public class MyClass : IDisposable
{
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // called via myClass.Dispose(). 
                // OK to use any private object references
            }
            // Release unmanaged resources.
            // Set large fields to null.                
            disposed = true;
        }
    }

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
}

Обычно CLR сохраняет вкладки на объектах с помощью финализатора, когда они создаются (что делает их более дорогими при создании). SuppressFinalize сообщает GC, что объект был очищен правильно, и ему не нужно идти в очередь финализатора. Он выглядит как деструктор C++, но не действует так, как он.

Оптимизация SuppressFinalize не тривиальна, так как ваши объекты могут долго жить в очереди финализатора. Не поддавайтесь искушению вызвать SuppressFinalize для других объектов. Это серьезный дефект, который должен произойти.

Рекомендации по проектированию сообщают нам, что финализатор не нужен, если ваш объект реализует IDisposable, но если у вас есть финализатор, вы должны реализовать IDisposable, чтобы разрешить детерминированную очистку вашего класса.

Большую часть времени вы должны иметь возможность сойти с IDisposable для очистки ресурсов. Финализатор вам нужен только тогда, когда ваш объект удерживает неуправляемые ресурсы, и вы должны гарантировать, что эти ресурсы очищены.

Примечание. Иногда кодеры добавляют финализатор для отладки сборок своих собственных классов IDisposable для проверки того, что код правильно разместил свой объект IDisposable.

    public void Dispose() // Implement IDisposable
    {
        Dispose(true);
    #if DEBUG
        GC.SuppressFinalize(this);
    #endif
    }

    #if DEBUG
    ~MyClass() // the finalizer
    {
        Dispose(false);
    }
    #endif

Вы говорите системе, что любая работа, которая была бы выполнена в финализаторе, уже выполнена, поэтому финализатор вызывать не нужно. Из документов.NET:

Объекты, которые реализуют интерфейс IDisposable, могут вызывать этот метод из метода IDisposable.Dispose, чтобы сборщик мусора не вызывал Object.Finalize для объекта, который ему не требуется.

В целом, большинство методов Dispose() должны вызывать GC.SupressFinalize(), потому что они должны очищать все, что будет очищено в финализаторе.

SupressFinalize - это просто то, что обеспечивает оптимизацию, которая позволяет системе не беспокоить очередь объекта в поток финализатора. Правильно написанный Dispose()/ финализатор должен работать должным образом с или без вызова GC.SupressFinalize().

 Dispose(true);
 GC.SuppressFinalize(this);

Если объект имеет финализатор, .net помещает ссылку в очередь финализации

Поскольку у нас есть вызов Dispose(ture), он очищает объект, поэтому нам не нужна очередь завершения для выполнения этой работы.

Поэтому вызовите GC.SuppressFinalize(this) для удаления ссылки в очереди завершения.

Этот метод должен вызываться в методе Dispose объектов, который реализует IDisposable, таким образом, GC не будет вызывать финализатор в другой раз, если кто-то вызовет метод Dispose.

Смотрите: http://msdn.microsoft.com/en-us/library/system.gc.suppressfinalize.aspx

Если класс или что-либо производное от него может содержать последнюю живую ссылку на объект с финализатором, то либо GC.SuppressFinalize(this) или же GC.KeepAlive(this) должен вызываться на объекте после любой операции, на которую может воздействовать этот финализатор, таким образом гарантируя, что финализатор не будет работать до тех пор, пока эта операция не будет завершена.

Цена GC.KeepAlive() а также GC.SuppressFinalize(this) по сути одинаковы в любом классе, который не имеет финализатора, и классы, которые имеют финализаторы, обычно должны вызывать GC.SuppressFinalize(this)так что использование последней функции в качестве последнего шага Dispose() может не всегда быть необходимым, но это не будет неправильно.

Другие вопросы по тегам