Какова цель GC.SuppressFinalize(this) в методе Dispose()?

У меня есть следующий код:

public void Dispose()
{
    if (_instance != null)
    {
        _instance = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
    }
}

Хотя есть комментарий, который объясняет цель этого вызова, связанного с ГК, я все еще не понимаю, почему он там есть.

Разве объект не предназначен для сборки мусора, когда все экземпляры перестают существовать, например, при использовании в using блок?

Какой сценарий использования, где это будет играть важную роль?

5 ответов

Решение

При реализации шаблона dispose вы также можете добавить в свой класс финализатор, который вызывает Dispose(), Это чтобы убедиться, что Dispose() всегда вызывается, даже если клиент забывает позвонить.

Чтобы предотвратить выполнение метода dispose дважды (если объект уже был удален), добавьте GC.SuppressFinalize(this);, Документация предоставляет образец:

class MyResource : IDisposable
{
    [...]

    // This destructor will run only if the Dispose method 
    // does not get called.
    ~MyResource()      
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue 
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed 
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up 
            // unmanaged resources here.
            resource.Cleanup()          
        }
        disposed = true;         
    }
}

Сборка мусора: GC восстанавливает память, используемую объектом, когда на объект больше не ссылаются.

Dispose: метод из интерфейса IDisposable, который освобождает все управляемые и неуправляемые ресурсы, когда программист вызывает его (прямо или косвенно через блок using).

Финализатор: метод для освобождения всех неуправляемых ресурсов. Вызывается GC до восстановления памяти.

Управляемый ресурс: любой класс.NET, который реализует IDisposable интерфейс, как потоки и DbConnections.

Неуправляемый ресурс: начинка в классах Managed Resource. Дескрипторы Windows - самые тривиальные примеры.


Теперь, чтобы ответить на ваш вопрос:

GC хранит список (очередь финализации) всех объектов, класс которых объявляет финализатор (~ClassName в C#). Объекты помещаются в эту очередь при создании. GC периодически запускается, чтобы проверить, есть ли какие-либо объекты, недоступные из программы. Затем он проверяет, имеются ли ссылки на какие-либо недоступные объекты из очереди финализации, и помещает их в другую очередь, называемую свободная очередь, в то время как остальные освобождаются. Отдельный поток используется для запуска методов Finalize объектов в очереди Freacheable.

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

SuppressFinalize Метод просто устанавливает флаг в заголовке объекта, который указывает, что не нужно запускать Финализатор. Таким образом, GC может сразу же восстановить память объекта. Согласно приведенному выше определению, Dispose Метод выполняет то же самое, что и финализатор (и более), поэтому, если он выполняется, финализация больше не является необходимой. С использованием SuppressFinalize метод, вы можете сохранить некоторую работу для GC, уведомив об этом факте. Кроме того, теперь вам не нужно реализовывать проверки в финализаторе, чтобы избежать двойного освобождения. Единственная проблема с Dispose то, что он не гарантированно запускается, потому что это обязанность программиста вызывать его, поэтому иногда нам нужно беспокоиться о Finalizer.


При этом, только очень и очень редко вам нужно написать Finalizer, потому что для подавляющего большинства обычных неуправляемых ресурсов управляемая оболочка уже существует, и управляемые ресурсы должны быть освобождены путем вызова их Dispose методы из вашего собственного Dispose метод, и только оттуда! В финализаторах вы никогда не должны вызывать метод Dispose.


Дальнейшее чтение:

Объекты, которые могут быть завершены, выживают при первом запуске GC.

Обычно, когда GC обнаруживает, что объект недоступен, он восстанавливает его. Если объект является финализуемым, то GC не возвращает его; вместо этого он считает его достижимым (и все объекты, на которые ссылается этот объект и т. д.) и планирует его для завершения. Объект будет восстановлен только тогда, когда он снова окажется недоступным в какой-то момент после его завершения.

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

Если ваш тип реализует финализатор (~MyType() { }), он не позволяет сборщику мусора запускать его. Используется, когда ваш финализатор заботится о неуправляемых типах, но пользователь уже вызвал Dispose() (либо явно, либо через using() { } блок), освобождая эти неуправляемые типы.

От MSDN: GC.SuppressFinalize:

Этот метод устанавливает бит в заголовке объекта, который система проверяет при вызове финализаторов. Параметр obj должен быть вызывающим для этого метода.

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

Обычно вы используете это, если ваш объект не ссылается на другие объекты, а только на отдельные типы, или уже сбросил любые ссылки на объекты на NULL.

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