Вам нужно избавиться от объектов и установить для них значение NULL, или сборщик мусора очистит их, когда они выйдут из области видимости?
310
Вам нужно избавиться от объектов и установить для них значение NULL, или сборщик мусора очистит их, когда они выйдут из области видимости?
Dispose()
метода! Это небольшая вариация в этом вопросе, но важная, потому что удаляемый объект не может знать, выходит ли он из области видимости (вызов неDispose()
является гарантией). Больше здесь: stackoverflow.com/questions/6757048/…Ответы:
Объекты будут очищены, когда они больше не используются и когда сборщик мусора сочтет нужным. Иногда вам может потребоваться установить объект
null
, чтобы он вышел из области видимости (например, статическое поле, значение которого вам больше не нужно), но в целом обычно нет необходимости устанавливать егоnull
.Что касается утилизации предметов, я согласен с @Andre. Если объект
IDisposable
является хорошим, рекомендуется утилизировать его, когда он вам больше не нужен, особенно если объект использует неуправляемые ресурсы. Неиспользование неуправляемых ресурсов приведет к утечкам памяти .Вы можете использовать
using
оператор для автоматического удаления объекта, как только ваша программа выйдет из области действияusing
оператора.Что функционально эквивалентно:
источник
if (obj != null) ((IDisposable)obj).Dispose();
IDisposable
. Если объект не удастся удалить, он, как правило, не вызовет утечку памяти ни у одного хорошо спроектированного класса. При работе с неуправляемыми ресурсами в C # у вас должен быть финализатор, который по-прежнему будет освобождать неуправляемые ресурсы. Это означает, что вместо освобождения ресурсов, когда это должно быть сделано, оно будет отложено до того момента, когда сборщик мусора завершит работу над управляемым объектом. Это все еще может вызвать много других проблем (таких как невыпущенные блокировки). Вы должны утилизироватьIDisposable
хотя!Объекты никогда не выходят из области видимости в C #, как в C ++. Они обрабатываются сборщиком мусора автоматически, когда они больше не используются. Это более сложный подход, чем C ++, где область действия переменной полностью детерминирована. Сборщик мусора CLR активно просматривает все созданные объекты и работает, если они используются.
Объект может выйти «из области видимости» в одной функции, но если его значение будет возвращено, то GC посмотрит, удерживает ли вызывающая функция возвращаемое значение.
Установка ссылок на объекты не
null
является необходимой, поскольку сборка мусора работает, определяя, на какие объекты ссылаются другие объекты.На практике вам не нужно беспокоиться о разрушении, оно просто работает и это здорово :)
Dispose
должен вызываться на всех объектах, которые реализуются,IDisposable
когда вы закончите работать с ними. Обычно вы используетеusing
блок с этими объектами, например:РЕДАКТИРОВАТЬ На переменной области. Крейг спросил, влияет ли переменная область действия на время жизни объекта. Чтобы правильно объяснить этот аспект CLR, мне нужно объяснить несколько понятий из C ++ и C #.
Фактическая область видимости переменной
В обоих языках переменную можно использовать только в той же области, в которой она была определена - класс, функция или блок операторов, заключенный в фигурные скобки. Тонкое отличие, однако, состоит в том, что в C # переменные не могут быть переопределены во вложенном блоке.
В C ++ это совершенно законно:
Однако в C # вы получаете ошибку компилятора:
Это имеет смысл, если вы посмотрите на сгенерированный MSIL - все переменные, используемые функцией, определены в начале функции. Посмотрите на эту функцию:
Ниже сгенерированный IL. Обратите внимание, что iVal2, который определен внутри блока if, фактически определен на уровне функций. Фактически это означает, что C # имеет только область действия класса и уровня функций, что касается времени жизни переменной.
Область действия C ++ и время жизни объекта
Всякий раз, когда переменная C ++, размещенная в стеке, выходит из области видимости, она разрушается. Помните, что в C ++ вы можете создавать объекты в стеке или в куче. Когда вы создаете их в стеке, когда выполнение выходит из области видимости, они выталкиваются из стека и уничтожаются.
Когда объекты C ++ создаются в куче, они должны быть явно уничтожены, иначе это утечка памяти. Нет такой проблемы с переменными стека, хотя.
Время жизни объекта C #
В CLR объекты (т.е. ссылочные типы) всегда создаются в управляемой куче. Это дополнительно подкрепляется синтаксисом создания объекта. Рассмотрим этот фрагмент кода.
В C ++ это создаст экземпляр
MyClass
в стеке и вызовет его конструктор по умолчанию. В C # это создаст ссылку на классMyClass
, который ни на что не указывает. Единственный способ создать экземпляр класса - использоватьnew
оператор:В некотором смысле, объекты C # во многом похожи на объекты, которые создаются с использованием
new
синтаксиса в C ++ - они создаются в куче, но в отличие от объектов C ++, они управляются средой выполнения, поэтому вам не нужно беспокоиться об их уничтожении.Поскольку объекты всегда находятся в куче, тот факт, что ссылки на объекты (т. Е. Указатели) выходят из области видимости, становится спорным. При определении необходимости сбора объекта требуется больше факторов, чем просто наличие ссылок на объект.
C # объект ссылки
Джон Скит сравнил ссылки на объекты в Java с кусочками строк, которые прикреплены к всплывающей подсказке, которая является объектом. Та же аналогия применима к ссылкам на объекты C #. Они просто указывают на местоположение кучи, содержащей объект. Таким образом, установка его в null не оказывает непосредственного влияния на время жизни объекта, баллон продолжает существовать до тех пор, пока GC не «вытолкнет» его.
Продолжая аналогию с воздушным шаром, представляется логичным, что, если к воздушному шару не прикреплены нити, его можно уничтожить. Фактически именно так работают объекты с подсчетом ссылок в неуправляемых языках. За исключением того, что этот подход не работает для циклических ссылок очень хорошо. Представьте себе два воздушных шарика, которые соединены друг с другом цепочкой, но ни у одного из них нет цепочки ни к чему другому. По простым правилам подсчета ссылок они оба продолжают существовать, даже если вся группа воздушных шаров «осиротела».
.NET объекты очень похожи на гелиевые шарики под крышей. Когда крыша открывается (GC бежит) - неиспользованные воздушные шары всплывают, хотя могут быть группы воздушных шаров, которые связаны друг с другом.
.NET GC использует комбинацию поколений GC и меток и разверток. Подход с использованием поколений включает в себя среду выполнения, предпочитающую проверять объекты, которые были выделены в последнее время, поскольку они с большей вероятностью будут неиспользованными, а разметка и развертка - во время выполнения, просматривая весь граф объектов и выясняя, есть ли группы объектов, которые не используются. Это адекватно решает проблему круговой зависимости.
Кроме того, .NET GC работает в другом потоке (так называемом потоке финализатора), так как у него есть немало дел, и выполнение этого в основном потоке прерывает вашу программу.
источник
Как уже говорили другие, вы определенно хотите позвонить,
Dispose
если класс реализуетIDisposable
. Я занимаю довольно жесткую позицию по этому вопросу. Некоторые могут утверждать , что вызовDispose
наDataSet
, к примеру, не имеет смысла , потому что они разобрали его и увидел , что он ничего содержательного не делать. Но я думаю, что в этом аргументе есть множество ошибок.Прочитайте это для интересной дискуссии уважаемых людей на эту тему. Тогда читайте мои рассуждения здесь , почему я думаю , что Джеффри Рихтер в неправильном лагере.
Теперь о том, следует ли вам установить ссылку на
null
. Ответ - нет. Позвольте мне проиллюстрировать мою точку зрения следующим кодом.Итак, когда вы думаете, объект, на который ссылается,
a
имеет право на коллекцию? Если вы сказали после звонка,a = null
то вы не правы. Если вы сказали, что после завершенияMain
метода вы также ошибаетесь. Правильный ответ: он может быть забран во время звонкаDoSomething
. Это верно. Он имеет право до того, как будет установлена ссылка,null
и, возможно, даже до завершения вызоваDoSomething
. Это связано с тем, что JIT-компилятор может распознавать, когда ссылки на объекты больше не разыменовываются, даже если они все еще укоренены.источник
a
это частное поле члена в классе? Еслиa
не установлено в ноль, GC не может знать,a
будет ли снова использоваться в каком-либо методе, верно? Таким образомa
, не будет собираться, пока не будет собран весь содержащий класс. Нет?a
был членом класса, а класс, содержащий его,a
был все еще укоренен и использовался, он бы тоже зависал. Это один из сценариев, где его установкаnull
может быть полезной.Dispose
это важно - невозможно вызватьDispose
(или любой другой не встроенный метод) объект без корневой ссылки на него; вызовDispose
после того, как объект был выполнен с использованием объекта, будет гарантировать, что корневая ссылка будет продолжать существовать в течение всего времени последнего действия над ним. Отказ от всех ссылок на объект без вызоваDispose
может по иронии судьбы привести к тому, что ресурсы объекта иногда высвобождаются слишком рано .Вам никогда не нужно устанавливать нулевые объекты в C #. Компилятор и среда выполнения позаботятся о том, чтобы выяснить, когда они больше не находятся в области видимости.
Да, вы должны избавляться от объектов, которые реализуют IDisposable.
источник
want
можете обнулить его, как только закончите, чтобы его можно было бесплатно восстанавливать.Я согласен с общим ответом здесь: да, вы должны утилизировать, и нет, вы вообще не должны устанавливать переменную в null ... но я хотел бы отметить, что утилизация - это НЕ в первую очередь управление памятью. Да, это может помочь (и иногда помогает) с управлением памятью, но его основная цель - дать вам детерминистическое освобождение дефицитных ресурсов.
Например, если вы открываете аппаратный порт (например, последовательный), сокет TCP / IP, файл (в режиме эксклюзивного доступа) или даже соединение с базой данных, вы теперь запретили любому другому коду использовать эти элементы, пока они не будут освобождены. Утилита обычно освобождает эти элементы (вместе с GDI и другими дескрипторами «os» и т. Д., Которые доступны тысячами, но в целом все еще ограничены). Если вы не вызываете dipose для объекта-владельца и не освобождаете эти ресурсы явным образом, попробуйте снова открыть тот же ресурс в будущем (или это делает другая программа), эта попытка открытия не удастся, поскольку в вашем нераспределенном, невыбранном объекте по-прежнему открыт элемент , Конечно, когда GC собирает элемент (если шаблон Dispose был реализован правильно), ресурс будет освобожден ... но вы не знаете, когда это произойдет, поэтому вы не Не знаю, когда будет безопасно снова открыть этот ресурс. Это основная проблема, с которой Dispose работает. Конечно, освобождение этих дескрипторов также часто освобождает память, и никогда не освобождая их, возможно, никогда не освободим эту память ... отсюда и все разговоры о утечках памяти или задержках очистки памяти.
Я видел реальные примеры этого вызывающего проблемы. Например, я видел веб-приложения ASP.Net, которые в конечном итоге не могут подключиться к базе данных (хотя и в течение коротких периодов времени или до тех пор, пока процесс веб-сервера не будет перезапущен), поскольку «пул соединений сервера sql заполнен» ... т.е. так много соединений было создано и явно не освобождено за столь короткий промежуток времени, что никакие новые соединения не могут быть созданы, и многие из соединений в пуле, хотя и не активны, по-прежнему ссылаются на необнаруженные и несобранные объекты и поэтому могут ' не может быть повторно использован. Правильное размещение соединений с базой данных, где это необходимо, гарантирует, что эта проблема не произойдет (по крайней мере, если у вас нет очень высокого одновременного доступа).
источник
Если объект реализует
IDisposable
, то да, вы должны распоряжаться им. Объект может висеть на собственных ресурсах (файловых дескрипторах, объектах ОС), которые иначе не могут быть освобождены немедленно. Это может привести к нехватке ресурсов, проблемам с блокировкой файлов и другим незначительным ошибкам, которых в противном случае можно было бы избежать.Смотрите также Реализация метода удаления в MSDN.
источник
Dispose
он будет вызван. Кроме того, если ваш объект удерживает дефицитный ресурс или блокирует какой-либо ресурс (например, файл), то вы захотите освободить его как можно скорее. Ожидание, пока GC сделает это, неоптимально.might
звони, аwill
звони.Если они реализуют интерфейс IDisposable, вы должны утилизировать их. Сборщик мусора позаботится обо всем остальном.
РЕДАКТИРОВАТЬ: лучше всего использовать
using
команду при работе с одноразовыми предметами:источник
Когда объект реализуется,
IDisposable
вы должны вызыватьDispose
(илиClose
, в некоторых случаях, это вызовет Dispose для вас).Обычно вам не нужно устанавливать объекты
null
, потому что GC будет знать, что объект больше не будет использоваться.Есть одно исключение, когда я устанавливаю объекты в
null
. Когда я получаю много объектов (из базы данных), над которыми мне нужно работать, и сохраняю их в коллекции (или массиве). Когда «работа» завершена, я устанавливаю объект наnull
, потому что GC не знает, что я закончил работу с ним.Пример:
источник
Обычно нет необходимости устанавливать поля в null. Тем не менее, я бы всегда рекомендовал избавляться от неуправляемых ресурсов.
По своему опыту я бы также посоветовал вам сделать следующее:
Я столкнулся с некоторыми очень трудными, чтобы найти проблемы, которые были прямым результатом того, что они не следовали совету выше.
Хорошее место для этого - Dispose (), но обычно лучше.
В общем, если существует ссылка на объект, сборщик мусора (GC) может занять пару поколений дольше, чтобы выяснить, что объект больше не используется. Все время объект остается в памяти.
Это может не быть проблемой, пока вы не обнаружите, что ваше приложение использует намного больше памяти, чем вы ожидаете. Когда это произойдет, подключите профилировщик памяти, чтобы увидеть, какие объекты не очищаются. Задание полей, ссылающихся на другие объекты как нулевые, и очистка коллекций при утилизации может действительно помочь GC выяснить, какие объекты он может удалить из памяти. GC восстановит использованную память быстрее, делая ваше приложение намного менее требовательным к памяти и быстрее.
источник
Всегда звоните утилизировать. Это не стоит риска. К крупным управляемым корпоративным приложениям следует относиться с уважением. Никакие предположения не могут быть сделаны, иначе он вернется, чтобы укусить вас.
Не слушай про Леппи.
Многие объекты на самом деле не реализуют IDisposable, поэтому вам не нужно беспокоиться о них. Если они действительно выйдут из области видимости, они будут автоматически освобождены. Также я никогда не сталкивался с ситуацией, когда мне приходилось устанавливать что-либо на ноль.
Одна вещь, которая может случиться, состоит в том, что много объектов можно держать открытыми. Это может значительно увеличить использование памяти вашего приложения. Иногда трудно понять, действительно ли это утечка памяти, или ваше приложение просто делает много вещей.
Инструменты профиля памяти могут помочь с такими вещами, но это может быть сложно.
Кроме того всегда отписывайтесь от событий, которые не нужны. Также будьте осторожны с привязкой WPF и элементами управления. Не обычная ситуация, но я столкнулся с ситуацией, когда у меня был элемент управления WPF, который был привязан к базовому объекту. Основной объект был большим и занимал большой объем памяти. Элемент управления WPF заменялся новым экземпляром, а старый почему-то все еще зависал. Это вызвало большую утечку памяти.
На заднем плане код был написан плохо, но дело в том, что вы хотите убедиться, что вещи, которые не используются, выходят за рамки. Это заняло много времени, чтобы найти с помощью профилировщика памяти, так как трудно понять, что в памяти является действительным, а что не должно быть там.
источник
Я тоже должен ответить. JIT генерирует таблицы вместе с кодом из статического анализа использования переменных. Эти записи таблицы являются «корнями GC» в текущем кадре стека. По мере продвижения указателя инструкций эти записи таблицы становятся недействительными и поэтому готовы к сборке мусора. Поэтому: если это переменная в области видимости, вам не нужно устанавливать ее в null - GC будет собирать объект. Если это член или статическая переменная, вы должны установить его на нуль
источник