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

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

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

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

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

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

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

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

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

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

Что касается ответа Фила Девани:

«… Calling Dispose позволяет делать детерминированную очистку и настоятельно рекомендуется».

На самом деле вызов Dispose () не детерминистически вызывает коллекцию GC в .NET, то есть он НЕ запускает GC немедленно только потому, что вы вызвали Dispose (). Он лишь косвенно сигнализирует GC о том, что объект может быть очищен во время следующего GC (для Генерации, в которой живет объект). Другими словами, если объект живет в Генерале 1, то он не будет удаляться, пока не произойдет assembly Gen 1. Один из единственных способов (хотя и не единственный), который вы можете программно и детерминированно заставить GC выполнять коллекцию, – это вызвать GC.Collect (). Однако делать это не рекомендуется, поскольку GC «настраивает» себя во время выполнения, собирая метрики о ваших распределениях памяти во время выполнения для вашего приложения. Вызов GC.Collect () сбрасывает эти показатели и заставляет GC снова начинать «настройку».

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

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

Это неполное. Поскольку GC не является детерминированным, доступен шаблон Dispose ( как правильно реализовать шаблон Dispose ), чтобы вы могли освобождать ресурсы, которые вы используете, – управляемые или неуправляемые. Это не имеет никакого отношения к тому, какие ресурсы вы выпускаете. Необходимость внедрения Finalizer имеет отношение к тому, какие ресурсы вы используете, т.е. ТОЛЬКО реализуете одно, если у вас есть нефинитируемые (т.е. собственные) ресурсы. Может быть, вы смешиваете этих двух. BTW, вы должны избегать реализации Finalizer, используя вместо этого class SafeHandle, который обертывает собственные ресурсы, которые маршалируются через P / Invoke или COM Interop. Если вы завершаете реализацию Finalizer, вы всегда должны реализовывать шаблон Dispose.

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

Если GC.SuppressFinalize () в конечном счете не вызывается, то финализатор для объекта будет вызываться в следующем GC. Обратите внимание, что правильная реализация шаблона Dispose должна вызвать GC.SuppressFinalize (). Таким образом, если вы вызываете Dispose () на объект, и он правильно реализовал шаблон, вы избежите выполнения Finalizer. Если вы не вызываете Dispose () на объект, который имеет финализатор, объект будет иметь свой Finalizer, выполняемый GC в следующей коллекции. Почему это плохо? Нить Finalizer в CLR вплоть до .NET 4.6 включает однопоточную. Представьте, что произойдет, если вы увеличите нагрузку на этот stream – производительность вашего приложения идет вам, где вы знаете.

Вызов Dispose на объекте предусматривает следующее:

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

Редактирование : я только заметил, что «всякая знающая и всегда правильная» документация MSDN на IDisposable (крайний сарказм здесь) действительно говорит

Основное использование этого интерфейса – освобождение неуправляемых ресурсов

Как всем известно, MSDN далека от правильной, никогда не упоминает или не показывает «лучшие практики», иногда предоставляет примеры, которые не компилируются и т. Д. К сожалению, это документировано в этих словах. Однако я знаю, что они пытались сказать: в идеальном мире GC очистит все управляемые ресурсы для вас (как идеалистические); он не будет, однако очищать неуправляемые ресурсы. Это абсолютно верно. Это, как говорится, жизнь не идеальна и ни одна из приложений. GC будет только очищать ресурсы, не имеющие корневых ссылок. В основном это проблема.

Среди 15-20 различных способов, с помощью которых .NET может «протекать» (или не бесплатной) памятью, тот, который, скорее всего, укусит вас, если вы не вызываете Dispose (), это неспособность отменить регистрацию / отменить / удалить / удалить событие обработчиках / делегатов. Если вы создаете объект, у которого есть делегированные ему проводники, и вы не вызываете Dispose () (и не отключаете сами delegates) на нем, GC все равно будет видеть объект как имеющий корневые ссылки – то есть delegates. Таким образом, GC никогда его не соберет.

@ комментарий joren’s / вопрос ниже (мой ответ слишком длинный для комментария):

У меня есть сообщение в блоге о шаблоне Dispose, который я рекомендую использовать – ( Как правильно реализовать шаблон Dispose ). Бывают случаи, когда вы должны исключать ссылки, и это никогда не мешает сделать это. Фактически, это делает что-то, прежде чем GC запускается – он удаляет корневую ссылку на этот объект. Затем GC проверяет свою коллекцию корневых ссылок и собирает те, которые не имеют корневой ссылки. Подумайте об этом примере, когда это хорошо: у вас есть экземпляр типа «ClassA» – назовем его «X». X содержит объект типа «ClassB» – назовем это «Y». Y реализует IDisposable, таким образом, X должен сделать то же самое, чтобы избавиться от Y. Предположим, что X находится в Generation 2 или LOH, а Y находится в поколении 0 или 1. Когда Dispose () вызывается на X и эта реализация завершает ссылка на Y, корневая ссылка на Y немедленно удаляется. Если GC для Gen 0 или Gen 1, память / ресурсы для Y очищается, но память / ресурсы для X не происходит, поскольку X живет в Gen 2 или LOH.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Interesting Posts

Подключение к удаленному рабочему столу Windows 10 с использованием учетных данных Azure AD

При одновременном подключении к проводной и беспроводной сети Windows 7 использует проводное соединение

Core Data vs SQLite 3

Функциональное, декларативное и императивное программирование

Ошибка раздувания classа CollapsingToolbarLayout

В чем разница между (этими четырьмя) состояниями сна?

Пример ASP.NET MVC для редактирования нескольких дочерних записей

Могу ли я переносить настольные приложения Windows 8 в современный интерфейс?

Частота обновления Adblock Chrome – позволяет пропускать некоторые объявления через

Как создать контекст JNDI в Spring Boot с встроенным контейнером Tomcat

Как вызвать функции в модели основного вида из других моделей представлений?

Определение пересечения двух сегментов линии?

Ошибка аутентификации при отладке VS2013 – iis express

Как стилизовать DrawerArrowToggle из библиотеки Android appcompat v7 21

Почему плохо использовать переменную итерации в выражении lambda

Давайте будем гением компьютера.