Зачем вызывать Dispose()? Утечка памяти не произойдет?

Изменить. Мой вопрос не получает основного ответа, который я искал. Я не был ясен. Мне бы очень хотелось узнать две вещи:

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

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

В обсуждении этой темы мое восприятие было неправильным; утечка памяти НЕ произойдет, если Dispose() не вызывается.

Зачем вообще звонить Dispose()? Это просто освободить ресурс немедленно, а не когда-нибудь позже? Что самое худшее, что может случиться, если у вас есть большая программа и никогда не вызывайте Dispose() для любого из ваших объектов IDisposable?

Ответ 1

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

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

Также разработчикам иногда полезно реализовать распоряжение для обеспечения детерминированной финализации - гарантируя порядок освобождения ресурсов.

Также обратите внимание, что классы, которые реализуют утилиту, часто также имеют финализатор для выпуска ресурсов, если Dispose не вызывается. Объекты с финализатором имеют иной жизненный цикл, чем классы без него. Когда они будут готовы к GC, GC увидит, что у них есть финализатор, и вместо того, чтобы немедленно собирать объект, когда GC готов, он помещает его в очередь финализации. Это означает, что объект живет за одну дополнительную GC-итерацию. Когда вы вызываете dispose, выполнение обычно (но не обязательно) вызывает GC.SuppressFinalize(), что означает, что финализатор больше не нужно вызывать.

Если класс реализует IDisposable, вы всегда должны вызывать Dispose().

Ответ 2

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

Некоторые объекты .NET имеют то, что называется "финализатор" - то, что вы можете определить и на своих собственных классах, но это редко встречается в типичном коде программиста на С#. Финализатор - это то, что выполняется, когда сборщик мусора уничтожает объект, а иногда он вызывается Dispose - но только в том случае, если разработчик этого класса сделал это.

Лучшая практика - всегда Dispose - несмотря ни на что. Существует множество библиотек, которые я использовал, когда не вызывающий Dispose ресурс приводит к утечке памяти, утечке соединения, утечке ресурсов операционной системы или другим видам ужаса. И сборщик мусора не будет устранять проблему, потому что они не реализуют какой-либо пользовательский финализатор.

Смотрите связанный: Будет ли вызов сборщика мусора IDisposable.Dispose для меня?

Ответ 3

Соглашение состоит в том, что если объект реализует IDisposable, вы должны вызвать Dispose() или использовать шаблон "using". Разница между Dispose() и ожиданием выполнения деструктора (финализатора) заключается в том, что Dispose() вызывается сразу и может использоваться для освобождения некоторых важных ресурсов, таких как соединения db, файлы, устройства, неуправляемые объекты и т.д.

Итак, чтобы суммировать - если это IDisposable - Dispose() это!

Ответ 4

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

Ответ 5

Для меня:

Dispose может использоваться при использовании области(). Это может помочь мне определить продолжительность жизни компонента IDisposeable. Обычно я использую это в классах StreamWriter/Reader или SqlConnection.

Другое использование Dispose заключается в том, что он может явно продлить срок службы компонента. Например, вызов Form.Dispose() в С# winform закроет форму. Однако для SqlConnection люди говорили, что просто вызов Dispose в одиночку без явного вызова Close не гарантирует закрытие соединения. Рекомендуем звонить как Close, так и Dispose. Я не пробовал это, хотя.

И еще, после вызова Dispose(), GC может немедленно освободить память, потому что они знают, что срок жизни объекта заканчивается, а не ждать окончания срока службы.

Другим вопросом может быть С# dispose IDisposable

Ответ 6

Не вызывать Dispose никогда не будет (* см. примечание 2 о неправильной реализации) вызывают традиционную утечку памяти (память никогда не освобождается до конца процесса).

"Единственное", что произойдет в отношении к памяти, будет освобождено в неопределенный момент в будущем.

Один интересный случай объектов Dispose - это когда очень маленькие управляемые объекты содержат большие объемы неуправляемой памяти (т.е. выделяются с некоторым вкусом функций управления памятью Win32, т.е. HeapAlloc). В этом случае менеджер управляемой памяти может не иметь возможности правильно определять давление памяти для запуска Gen2 GC и (особенно в случае x86 - 32-битного процесса), он может преждевременно не выделять управляемую память для вашего процесса. Другой проблемой в этом случае является фрагментация адресного пространства, "ожидая, что GC будет выделен" (опять-таки, главным образом, в случае x86) - когда меньшие фрагменты собственной памяти выделяются некоторым относительно большим пространством между ними, предотвращая выделение больших блоков необходимых для управления управляемой памятью.

Примечания:

  • В этом ответе явно говорится о истинных утечках памяти/проблемах с распределением памяти, исключаемых использованием IDisposable объекта управления памятью. Хотя верно, что в результате такой практики нет "истинных утечек памяти", большинство людей будут рассматривать увеличение использования памяти в качестве утечки памяти (аналогично хранению большого количества объектов в статическом списке/словарях на всю жизнь приложения).
  • Можно создать объект, который управляет собственной памятью и неправильно реализует шаблон IDisposable. В этом случае можно реально протекать собственная память (независимо от вызова Dispose).
  • В большинстве случаев объекты, реализующие IDisposable, вообще не управляют памятью. Для большинства практических С#-программ собственные ресурсы, управляемые такими объектами, являются ручками для системных ресурсов, таких как файлы, растровые изображения, шрифты, объекты синхронизации или собственные объекты COM. Неиспользование их своевременно приведет к другим проблемам.

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

Ответ 7

Невозможно вызвать Dispose() вызвать утечку памяти?

Да, конечно. Ниже приведен лишь один пример.

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

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

Худший случай не освобождает какую-то нежелательную память, пока вы не закроете приложение.

И другой вопрос: что, если вы никогда не реализуете IDisposable или финализацию, когда они требуются?

Худший случай наличия утечки памяти удерживается в этой памяти до перезапуска ПК. Это может произойти только в том случае, если у вас есть неуправляемые ресурсы, и вы не реализуете dispose/finalize. Если вы реализуете интерфейс Idisposable и реализуете финализатор, процесс финализации будет выполнять Dispose для вас.

Еще одна причина, по которой вы должны вызвать Dispose, - это подавление финализации.

Как я уже указывал, есть ли какой-либо объект с методом Finalize, и вы не вызывали Dispose. Этот объект может находиться в памяти для двух циклов GC. В первом цикле он помещает этот экземпляр в очередь финализации, и завершение происходит после процесса GC. Таким образом, только следующий цикл GC может освободить эту память.