Since.NET есть сборщик мусора зачем нам нужны финализаторы / деструкторы / dispose-pattern?


Если я правильно понимаю, среда выполнения .net всегда будет очищаться после меня. Поэтому, если я создаю новые объекты и перестаю ссылаться на них в своем коде, среда выполнения очистит эти объекты и освободит память, которую они занимали.

Так как это так, почему тогда некоторые объекты должны иметь деструктор или метод dispose? Не будет ли среда выполнения очищать после них, когда они больше не ссылаются?

12 67

12 ответов:

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

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

простой пример того, почему Dispose нужен может быть быстрый и грязный способ входа:

public void Log(string line)
{
    var sw = new StreamWriter(File.Open(
        "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None));

    sw.WriteLine(line);

    // Since we don't close the stream the FileStream finalizer will do that for 
    // us but we don't know when that will be and until then the file is locked.
}

в приведенном выше примере файл будет оставайтесь заблокированными, пока сборщик мусора не вызовет финализатор на

предыдущие ответы хороши, но позвольте мне еще раз подчеркнуть важный момент. В частности, вы сказали, что

Если я правильно понимаю, среда выполнения .net всегда будет очищаться после меня.

Это только отчасти правильно. На самом деле,.NETтолько предлагает автоматическое управление для одного конкретного ресурса: основной памяти. Все остальные ресурсы требуют ручной очистки.1)

Как ни странно, главный память получает особый статус практически во всех дискуссиях о программных ресурсах. Конечно, для этого есть веская причина – основная память часто является самым дефицитным ресурсом. Но стоит помнить, что есть и другие типы ресурсов, которые также нуждаются в управлении.


1) обычно предпринимается попытка связать время жизни других ресурсов с временем жизни ячеек памяти или идентификаторов в коде-следовательно, существование финализаторов.

сборщик мусора будет работать только в том случае, если система не находится под давлением памяти, если ей действительно не нужно освободить некоторую память. Это означает, что вы никогда не можете быть уверены, когда GC будет работать.

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

Как правило, IDisposable реализуется на любом классе, который работает с неуправляемыми ресурсами.

  1. есть вещи, которые сборщик мусора не может очистить после вас
  2. даже с вещами, он можете очистка, вы можете помочь ему очистить раньше

реальная причина заключается в том, что сборка мусора .net не предназначена для сбора неуправляемые ресурсы, поэтому очистка этих ресурсов все еще находится в руках разработчика. Кроме того, завершители объектов не вызываются автоматически, когда объект выходит за пределы области видимости. Они вызываются ГК в какое-то неопределенное время. И когда им звонят, GC не запускает его сразу, он ждет следующего раунда, чтобы позвонить ему, увеличивая время, чтобы очистить еще больше, а не хорошо вещь, когда ваши объекты содержат скудные неуправляемые ресурсы (такие как файлы или сетевые подключения). Введите одноразовый шаблон, где разработчик может вручную освободить дефицитные ресурсы в определенное время (при звонке yourobject.Dispose() или использование(...) заявление.) Имейте в виду, что вы должны позвонить в GC.SuppressFinalize (this); в вашем методе dispose чтобы сообщить GC, что объект был удален вручную и не должен быть завершен. Я предлагаю вам взглянуть на основных принципах проектирования книга К. Квалина и Б. Абрамса. Это объясняет одноразовый шаблон очень хорошо.

Удачи!

простое объяснение:

  • Dispose предназначен для детерминированные утилизация ресурсов, не связанных с памятью, особенно ограниченные ресурсы. Например, дескриптор окна или подключение к базе данных.
  • Finalize предназначен для недетерминированный удаление ресурсов, не связанных с памятью, обычно в качестве резервной копии, если Dispose не был вызван.

некоторые рекомендации по реализации Finalize метод:

  • реализовать Finalize только для объектов, требующих завершения, поскольку существует стоимость производительности, связанная с методами Finalize.
  • Если вам требуется метод Finalize, рассмотрите возможность реализации IDisposable, чтобы пользователи вашего типа могли избежать затрат на вызов метода Finalize.
  • ваши методы Finalize должны быть защищены, а не общедоступны.
  • ваш метод Finalize должен освободить все внешние ресурсы, которые тип владеет, но только те, что ему принадлежат. Он не должен ссылаться на какие-либо другие ресурсы.
  • среда CLR не дает никаких гарантий относительно порядка, в котором вызываются методы Finalize. Как отмечает Даниэль в своем комментарии, это означает, что метод Finalize не должен обращаться к каким-либо ссылочным типам членов, если это возможно, потому что они могут иметь (или могут когда-нибудь иметь) свои собственные финализаторы.
  • никогда не вызывайте метод Finalize непосредственно для любого типа, отличного от базового типа типа.
  • старайтесь избегать любых необработанных исключений в вашем методе Finalize, так как это завершит ваш процесс (в 2.0 или выше).
  • избегайте выполнения какой-либо длительной задачи в вашем методе финализатора, так как это заблокирует поток финализатора и предотвратит выполнение других методов финализатора.

некоторые рекомендации по реализации метода Dispose:

  • реализовать шаблон проектирования dispose на тип, который инкапсулирует ресурсы, которые явно нужно освободиться.
  • реализуйте шаблон проектирования dispose на базовом типе, который имеет один или несколько производных типов, которые удерживают ресурсы, даже если базовый тип этого не делает.
  • после того, как Dispose был вызван на экземпляре, предотвратите запуск метода Finalize, вызвав GC.Метод SuppressFinalize. Единственным исключением из этого правила является редкая ситуация, в которой работа должна быть выполнена в Finalize, которая не охватывается Dispose.
  • не предполагайте, что утилизировать будут звать. Неуправляемые ресурсы, принадлежащие типу, также должны быть освобождены в методе Finalize в случае, если Dispose не вызывается.
  • бросьте исключение ObjectDisposedException из методов экземпляра этого типа (кроме Dispose), когда ресурсы уже удалены. Это правило не применяется к методу Dispose, поскольку он должен вызываться несколько раз без создания исключения.
  • распространение вызовов Dispose через иерархию базовых типов. The Dispose метод должен освободить все ресурсы, принадлежащие этому объекту, и любой объект, принадлежащий этому объекту.
  • вы должны рассмотреть вопрос о том, чтобы не разрешать использование объекта после вызова метода Dispose. Воссоздание объекта, который уже был удален, является сложным шаблоном для реализации.
  • разрешить метод Dispose вызываться несколько раз без создания исключения. Метод не должен ничего делать после первого вызова.

объекты, которым нужны дескрукторы и методы dispose, используют неуправляемые ресурсы. Таким образом, сборщик мусора не может очистить эти ресурсы, и вы должны сделать это самостоятельно.

посмотрите на документацию MSDN для IDisposable;http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

в примере используется неуправляемый обработчик-IntPr.

некоторые объекты могут потребоваться для очистки низкоуровневых элементов. Например, оборудование, которое должно быть закрыто и т. д.

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

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

таким образом, в общем случае вам не нужно будет использовать шаблон Dispose/Finalize, если вы не используете неуправляемые ресурсы.

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

сборщик мусора .NET знает, как обрабатывать управляемые объекты в среде выполнения .NET. Но шаблон Dispose (IDisposable) используется в основном для неуправляемых объектов, которые использует приложение.

другими словами, среда выполнения .NET не обязательно знает, как работать с каждым типом устройства или обрабатывать его (закрытие сетевых подключений, дескрипторов файлов, графических устройств и т. д.), Поэтому использование IDisposable позволяет сказать "позвольте мне реализовать некоторую собственную очистку" в a тип. Увидев эту реализацию, сборщик мусора может вызвать метод Dispose () и обеспечить очистку объектов за пределами управляемой кучи.