Какова цель 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.