Здесь необходимо внести пару исправлений:
Относительно ответа Фила Девани:
"... Вызов Dispose позволяет выполнять детерминированную очистку и настоятельно рекомендуется."
На самом деле, вызов Dispose () не приводит к детерминированному вызову коллекции GC в .NET, то есть он НЕ запускает GC немедленно только потому, что вы вызвали Dispose (). Это только косвенно сигнализирует GC, что объект может быть очищен во время следующего GC (для Поколения, в котором этот объект живет). Другими словами, если объект живет в Gen 1, то он не будет утилизирован до тех пор, пока не будет произведена коллекция Gen 1. Единственный способ (хотя и не единственный), который вы можете программно и детерминистически заставить GC выполнить коллекцию, - это вызвать GC.Collect (). Однако делать это не рекомендуется, поскольку GC «настраивается» во время выполнения, собирая метрики о распределении памяти во время выполнения для вашего приложения. Вызов GC.Collect () сбрасывает эти метрики и заставляет GC заново начинать «настройку».
Относительно ответа:
IDisposable предназначен для утилизации неуправляемых ресурсов. Это шаблон в .NET.
Это неполно. Поскольку GC недетерминирован, доступен шаблон Dispose ( Как правильно реализовать шаблон Dispose ), чтобы вы могли высвободить используемые ресурсы - управляемые или неуправляемые. Он не имеет никакого отношения к тому, какие ресурсы вы выпускаете. Необходимость реализации Финализатора связана с тем, какие ресурсы вы используете, т. Е. Реализуете ТОЛЬКО один, если у вас есть не финализируемые (то есть нативные) ресурсы. Может быть, вы путаете их. Кстати, вам следует избегать реализации Finalizer, используя вместо этого класс SafeHandle, который оборачивает собственные ресурсы, которые маршалируются через P / Invoke или COM Interop. Если вы в конечном итоге внедрите Финализатор, вы должны всегда реализовать шаблон утилизации.
Одно критическое замечание, о котором я еще никого не упомянул, это то, что если создается одноразовый объект и у него есть финализатор (и вы никогда не знаете, действительно ли он есть - и вы, конечно, не должны делать никаких предположений относительно то), то он будет отправлен непосредственно в очередь на финализацию и будет доступен как минимум для 1 дополнительной коллекции GC .
Если в конечном итоге GC.SuppressFinalize () не вызывается, то на следующем GC будет вызван финализатор для объекта. Обратите внимание, что правильная реализация шаблона Dispose должна вызывать GC.SuppressFinalize (). Таким образом, если вы вызовите Dispose () для объекта, и он правильно реализовал шаблон, вы избежите выполнения Finalizer. Если вы не вызываете Dispose () для объекта, у которого есть финализатор, у объекта будет финализатор, выполняемый GC в следующей коллекции. Почему это плохо? Финализатор потока в CLR до .NET 4.6 включительно является однопоточным. Представьте себе, что произойдет, если вы увеличите нагрузку на этот поток - производительность вашего приложения зависит от того, где вы знаете.
Вызов Dispose для объекта предусматривает следующее:
- уменьшить нагрузку на ГХ для процесса;
- уменьшить нагрузку на память приложения;
- уменьшить вероятность возникновения OutOfMemoryException (OOM), если LOH (куча больших объектов) фрагментируется и объект находится на LOH;
- Держите объект вне финализируемой и f-достижимой очередей, если у него есть финализатор;
- Убедитесь, что ваши ресурсы (управляемые и неуправляемые) очищены.
Редактировать :
Я только что заметил, что «всезнающая и всегда правильная» документация MSDN по IDisposable (крайний сарказм здесь) на самом деле говорит
Основное использование этого интерфейса
освободить неуправляемые ресурсы
Как должен знать каждый, MSDN далек от правильности, никогда не упоминает и не показывает «лучшие практики», иногда предоставляет примеры, которые не компилируются и т. Д. К сожалению, это задокументировано в этих словах.Тем не менее, я знаю, что они пытались сказать: в идеальном мире GC очистит все управляемые ресурсы для вас (насколько идеалистично);однако он не будет очищать неуправляемые ресурсы.Это абсолютно верно.Тем не менее, жизнь не идеальна и не является приложением. GC будет очищать только те ресурсы, у которых нет корневых ссылок. Это, в основном, проблема.
Среди примерно 15-20 различных способов, которыми .NET может «вытекать» (или нет)свободной) памяти, которая, скорее всего, укусила бы вас, если бы вы не вызывали Dispose () - это ошибка отмены регистрации / отсоединения / отмены / отсоединения обработчиков / делегатов событий.Если вы создаете объект, к которому подключены делегаты, и вы не вызываете Dispose () (и не отсоединяете делегатов самостоятельно), GC все равно увидит объект с корневыми ссылками - то есть делегатов.Таким образом, сборщик мусора никогда его не соберет.
@ комментарий / вопрос joren ниже (мой ответ слишком длинный для комментария):
У меня есть запись в блоге о шаблоне Dispose, который я рекомендуюиспользуйте - ( Как правильно реализовать шаблон Dispose ).Есть моменты, когда вы должны аннулировать ссылки, и это никогда не повредит.На самом деле это делает что-то перед запуском GC - он удаляет корневую ссылку на этот объект.Позднее GC сканирует свою коллекцию корневых ссылок и собирает те, у которых нет корневой ссылки.Подумайте об этом примере, когда это хорошо: у вас есть экземпляр типа «ClassA» - назовем его «X».X содержит объект типа «ClassB» - назовем это «Y».Y реализует IDisposable, таким образом, X должен сделать то же самое, чтобы избавиться от Y. Предположим, что X находится в поколении 2 или LOH, а Y находится в поколении 0 или 1. Когда Dispose () вызывается для X, и эта реализация обнуляетссылка на Y, корневая ссылка на Y немедленно удаляется.Если GC происходит для Gen 0 или Gen 1, память / ресурсы для Y очищены, но память / ресурсы для X нет, так как X живет в Gen 2 или LOH.