Что произойдет, если я не вызову Dispose на объекте пера? - PullRequest
35 голосов
/ 24 ноября 2010

Что произойдет, если я не вызову Dispose для объекта pen в этом фрагменте кода?

private void panel_Paint(object sender, PaintEventArgs e)
{
    var pen = Pen(Color.White, 1);
    //Do some drawing
}

Ответы [ 10 ]

27 голосов
/ 05 апреля 2011

Здесь необходимо внести пару исправлений:

Относительно ответа Фила Девани:

"... Вызов 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 для объекта предусматривает следующее:

  1. уменьшить нагрузку на ГХ для процесса;
  2. уменьшить нагрузку на память приложения;
  3. уменьшить вероятность возникновения OutOfMemoryException (OOM), если LOH (куча больших объектов) фрагментируется и объект находится на LOH;
  4. Держите объект вне финализируемой и f-достижимой очередей, если у него есть финализатор;
  5. Убедитесь, что ваши ресурсы (управляемые и неуправляемые) очищены.

Редактировать : Я только что заметил, что «всезнающая и всегда правильная» документация 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.

24 голосов
/ 24 ноября 2010

Pen будет собираться GC в какой-то неопределенной точке в будущем, независимо от того, звоните вы или нет Dispose.

Однако любые неуправляемые ресурсы, удерживаемые ручкой (например, GDI +ручкой) не будет очищен GC.GC только очищает управляемые ресурсы.Вызов Pen.Dispose позволяет вам обеспечить своевременную очистку этих неуправляемых ресурсов и отсутствие утечек ресурсов.

Теперь, если Pen имеет финализатор и этот финализатор очищаетнеуправляемые ресурсы, после чего эти неуправляемые ресурсы будут очищены при сборке мусора Pen.Но дело в том, что:

  1. Вы должны явно позвонить Dispose, чтобы освободить неуправляемые ресурсы, и
  2. Вам не нужно беспокоиться о деталях реализации ifесть финализатор, который очищает неуправляемые ресурсы.

Pen реализует IDisposable.IDisposable для утилизации неуправляемых ресурсов.Это шаблон в .NET.

Предыдущие комментарии по этой теме см. В ответе .

.
13 голосов
/ 24 ноября 2010

Базовый дескриптор пера GDI + не будет освобожден до некоторого неопределенного времени в будущем, то есть когда объект Pen будет собирать мусор и вызывается финализатор объекта. Это может не произойти, пока процесс не завершится, или это может быть раньше, но дело в том, что он недетерминирован. Вызов Dispose позволяет выполнять детерминированную очистку и настоятельно рекомендуется.

2 голосов
/ 24 ноября 2010

Общий объем используемой памяти .Net составляет часть .Net + все используемые «внешние» данные. Объекты ОС, открытые файлы, базы данных и сетевые подключения - все они требуют ресурсов, которые не являются чисто объектами .Net.

Графика использует ручки и другие объекты, которые на самом деле являются объектами ОС, которые довольно дороги для хранения. (Вы можете поменять ручку на файл растрового изображения 1000x1000). Эти объекты ОС удаляются из памяти ОС только после вызова определенной функции очистки. Функции Pen и Bitmap Dispose делают это для вас сразу же, когда вы их вызываете.

Если вы не позвоните в компанию Dispose, сборщик мусора придет, чтобы очистить их «где-то в будущем». (Это на самом деле вызовет деструктор / завершающий код, который, вероятно, вызывает Dispose ())

* на машине с бесконечной памятью (или более 1 ГБ) где-то в будущем может быть очень далеко в будущем. На машине, которая ничего не делает, можно легко потратить больше 30 минут на очистку этого огромного растрового изображения или очень маленькой ручки.

2 голосов
/ 24 ноября 2010

Если вы действительно хотите знать, насколько плохо, когда вы не вызываете Dispose для графических объектов, вы можете использовать CLR Profiler, доступный бесплатно для загрузки здесь. В папке установки (по умолчанию C: \ CLRProfiler) - это CLRProfiler.doc, который имеет хороший пример того, что происходит, когда вы не вызываете Dispose для объекта Brush. Это очень поучительно. Вы также можете прочитать об использовании IDisposable здесь и здесь .

1 голос
/ 24 ноября 2010

Зависит, если он реализует финализатор и вызывает метод Dispose для своего метода финализации. Если так, ручка будет выпущена в GC.

, если нет, дескриптор будет оставаться до тех пор, пока процесс не будет остановлен.

1 голос
/ 24 ноября 2010

Он сохранит ресурсы, пока сборщик мусора не очистит его

0 голосов
/ 24 ноября 2010

С графическим материалом это может быть очень плохо.

Откройте диспетчер задач Windows. Нажмите «выбрать столбцы» и выберите столбец с именем «Объекты GDI».

Если вы не располагаете определенными графическими объектами, это число будет расти и расти.

В старых версиях Windows это может привести к сбою всего приложения (насколько я помню, предел был 10000), хотя я не уверен насчет Vista / 7, но это все равно плохо.

0 голосов
/ 24 ноября 2010

в глубине души первая идея появилась на поверхности, что этот объект будет удален, как только метод завершит выполнение !, я не знаю, откуда я получил эту информацию !, это правда?

0 голосов
/ 24 ноября 2010

сборщик мусора будет собирать его в любом случае, НО это имеет значение КОГДА: если вы не вызовите dispose для объекта, который вы не используете, он будет жить дольше в памяти и будет переведен в более старшие поколения, что означает, что его сбор будет более дорогим.

...