Какова цель GC.SuppressFinalize (this) в методе Dispose ()? - PullRequest
28 голосов
/ 14 июня 2010

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

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 ]

33 голосов
/ 14 июня 2010

При реализации шаблона 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;         
    }
}
31 голосов
/ 14 июня 2010

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

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

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

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

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


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

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

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

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


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


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

6 голосов
/ 14 июня 2010

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

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

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

2 голосов
/ 14 июня 2010

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

0 голосов
/ 14 июня 2010

Из MSDN: GC.SuppressFinalize :

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

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

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

...