Вам нужно избавляться от объектов и устанавливать их в null?
Нужно ли вам распоряжаться объектами и устанавливать их в null или сборщик мусора очистит их, когда они выйдут из сферы действия?
- В Java собрано мусор пространства постоянного поколения?
- Как сборщик мусора Java обрабатывает саморекламу?
- Что такое сборщик мусора в Java?
- Является ли настройка объектов Java на null больше чем-либо?
- Сборщик мусора Java - Когда он собирает?
- GC.Collect ()
- Что же случилось с использованием GC.Collect ()?
- Когда метод finalize () вызывается в Java?
Объекты будут очищены, когда они больше не используются и когда сборщик мусора сочтет нужным. Иногда вам может потребоваться установить объект в null
, чтобы исключить его из области действия (например, статическое поле, значение которого вам больше не нужно), но обычно нет необходимости устанавливать значение null
.
Что касается объектов размещения, я согласен с @Andre. Если объект является IDisposable
рекомендуется избавиться от него, когда он вам больше не нужен, особенно если объект использует неуправляемые ресурсы. Не избавление от неуправляемых ресурсов приведет к утечке памяти .
Вы можете использовать оператор using
для автоматического удаления объекта после того, как ваша программа покинет область действия оператора using
.
using (MyIDisposableObject obj = new MyIDisposableObject()) { // use the object here } // the object is disposed here
Что функционально эквивалентно:
MyIDisposableObject obj; try { obj = new MyIDisposableObject(); } finally { if (obj != null) { ((IDisposable)obj).Dispose(); } }
Объекты никогда не выходят за пределы области действия на C #, как в C ++. Они обрабатываются сборщиком мусора автоматически, когда они больше не используются. Это более сложный подход, чем C ++, где область действия переменной полностью детерминирована. Сборщик мусора CLR активно проходит через все объекты, которые были созданы и работают, если они используются.
Объект может «выходить из области видимости» в одной функции, но если его значение возвращается, тогда GC будет смотреть на то, будет ли функция вызова удерживаться на возвращаемом значении.
Установка ссылок на объекты на null
требуется, поскольку garbage collection работает, определяя, на какие объекты ссылаются другие объекты.
На практике вам не нужно беспокоиться об уничтожении, это просто работает, и это здорово 🙂
Dispose
должен быть вызван всеми объектами, которые реализуют IDisposable
когда вы закончите работать с ними. Обычно вы использовали бы using
блок с такими объектами:
using (var ms = new MemoryStream()) { //... }
EDIT В переменной области. Крейг спросил, влияет ли область видимости переменной на время жизни объекта. Чтобы правильно объяснить этот аспект CLR, мне нужно будет объяснить несколько концепций из C ++ и C #.
Действительная область переменных
В обоих языках переменная может использоваться только в той же области, в которой она была определена – class, функция или блок оператора, заключенный в фигурные скобки. Однако тонкая разница заключается в том, что в C # переменные не могут быть переопределены во вложенном блоке.
В C ++ это совершенно законно:
int iVal = 8; //iVal == 8 if (iVal == 8){ int iVal = 5; //iVal == 5 } //iVal == 8
В C #, однако вы получаете ошибку компилятора aa:
int iVal = 8; if(iVal == 8) { int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else }
Это имеет смысл, если вы посмотрите на сгенерированную MSIL – все переменные, используемые функцией, определены в начале функции. Взгляните на эту функцию:
public static void Scope() { int iVal = 8; if(iVal == 8) { int iVal2 = 5; } }
Ниже представлен сгенерированный ИЛ. Обратите внимание, что iVal2, который определен внутри блока if, фактически определен на уровне функции. Эффективно это означает, что C # обладает только областью уровня classа и уровня в отношении переменной продолжительности жизни.
.method public hidebysig static void Scope() cil managed { // Code size 19 (0x13) .maxstack 2 .locals init ([0] int32 iVal, [1] int32 iVal2, [2] bool CS$4$0000) //Function IL - omitted } // end of method Test2::Scope
Область C ++ и время жизни объекта
Всякий раз, когда переменная C ++, выделенная в стеке, выходит из области действия, она разрушается. Помните, что в C ++ вы можете создавать объекты в стеке или в куче. Когда вы создаете их в стеке, как только выполнение покидает область действия, они выходят из стека и уничтожаются.
if (true) { MyClass stackObj; //created on the stack MyClass heapObj = new MyClass(); //created on the heap obj.doSomething(); } //<-- stackObj is destroyed //heapObj still lives
Когда объекты C ++ создаются в куче, они должны быть явно уничтожены, иначе это утечка памяти. Однако такая проблема с переменными стека не существует.
C # Object Lifetime
В CLR объекты (т.е. ссылочные типы) всегда создаются на управляемой куче. Это дополнительно подкрепляется синтаксисом создания объекта. Рассмотрим этот fragment кода.
MyClass stackObj;
В C ++ это создаст экземпляр MyClass
в стеке и вызовет его конструктор по умолчанию. В C # он создаст ссылку на class MyClass
, который не указывает ни на что. Единственный способ создать экземпляр classа - использовать new
оператор:
MyClass stackObj = new MyClass();
В некотором смысле, объекты C # очень похожи на объекты, созданные с использованием new
синтаксиса на C ++, - они создаются в куче, но в отличие от объектов C ++ они управляются средой выполнения, поэтому вам не нужно беспокоиться о ее разрушении.
Так как объекты всегда находятся в куче, тот факт, что ссылки на объекты (то есть указатели) выходят из области видимости, становится спорным. Существует множество факторов, определяющих, нужно ли собирать объект, а не просто наличие ссылок на объект.
Ссылки на C #
Джон Скит сравнивал ссылки на объекты в Java на куски строки, которые прикреплены к баллу, который является объектом. Такая же аналогия применяется к ссылкам на объекты C #. Они просто указывают на местоположение кучи, которая содержит объект. Таким образом, установка его в нуль не оказывает немедленного влияния на время жизни объекта, баллон продолжает существовать, пока GC «не всплывает».
Продолжая аналогию с баллонами, было бы логично, что, как только воздушный шар не имеет никаких привязок к нему, он может быть уничтожен. На самом деле это именно то, как ссылки подсчитанных объектов работают на не управляемых языках. Кроме того, этот подход не очень хорошо работает для циркулярных ссылок. Представьте себе два воздушных шара, которые соединяются струной, но ни один воздушный шар не имеет нитки. В простых правилах подсчета голосов оба они продолжают существовать, хотя вся воздушная шара «осиротела».
Объекты .NET очень похожи на воздушные шары гелия под крышей. Когда крыша открывается (GC работает) - неиспользуемые воздушные шары уплывают, хотя могут быть группы воздушных шаров, которые связаны друг с другом.
.NET GC использует комбинацию GC поколения и метки и развертки. Подход, основанный на генерации, включает время выполнения, которое позволяет проверять объекты, которые были выделены совсем недавно, поскольку они более вероятно не используются, а отметка и развертка включает время выполнения, проходящее через весь графический объект, и разработку, если существуют неиспользуемые группы объектов. Это адекватно относится к проблеме циклической зависимости.
Кроме того, .NET GC работает в другом streamе (так называемом streamе финализатора), так как он имеет довольно много дел, и выполнение этого в основном streamе прерывает вашу программу.
Как говорили другие, вы определенно хотите вызвать Dispose
если class реализует IDisposable
. Я занимаю довольно жесткую позицию по этому вопросу. Некоторые могут утверждать, что вызов Dispose
в DataSet
, например, бессмысленен, потому что он разобрал его и увидел, что он ничего не делает значимым. Но, я думаю, в этом аргументе есть заблуждения.
Прочтите это для интересной дискуссии уважаемых людей по этому вопросу. Затем прочтите мои рассуждения, почему я думаю, что Джеффри Рихтер находится в неправильном лагере.
Теперь, нужно ли вам установить ссылку на null
. Ответ – нет. Позвольте мне проиллюстрировать мою мысль следующим кодом.
public static void Main() { Object a = new Object(); Console.WriteLine("object created"); DoSomething(a); Console.WriteLine("object used"); a = null; Console.WriteLine("reference set to null"); }
Итак, когда вы думаете, что объект, на который ссылается a
имеет право на сбор? Если вы сказали после вызова a = null
вы ошибаетесь. Если вы сказали после завершения метода Main
вы также ошибаетесь. Правильный ответ заключается в том, что он может собираться когда-нибудь во время вызова DoSomething
. Это верно. Он имеет право до того, как ссылка будет установлена в null
и, возможно, даже до завершения вызова DoSomething
. Это связано с тем, что компилятор JIT может распознавать, когда ссылки на объекты больше не разыменовываются, даже если они все еще внедрены.
Вам никогда не нужно устанавливать объекты на null в C #. Компилятор и среда выполнения позаботятся о том, чтобы выяснить, когда они больше не охвачены.
Да, вы должны избавиться от объектов, которые реализуют IDisposable.
Я согласен с общим ответом здесь, что да, вы должны распоряжаться, и вы, как правило, не должны устанавливать переменную в null … но я хотел бы указать, что dispose НЕ относится главным образом к управлению памятью. Да, он может помочь (и иногда делает) с управлением памятью, но основной целью является дать вам детерминированное освобождение ограниченных ресурсов.
Например, если вы открываете аппаратный порт (например, серийный), сокет TCP / IP, файл (в режиме эксклюзивного доступа) или даже соединение с базой данных, которое вы теперь предотвратили, чтобы какой-либо другой код использовал эти элементы до их выпуска. Dispose обычно выпускает эти элементы (наряду с GDI и другими «os» ручками и т. Д., Которые есть 1000 доступных, но все еще ограничены в целом). Если вы не вызываете дипос на объекте владельца и явно освобождаете эти ресурсы, попробуйте снова открыть тот же ресурс в будущем (или другую программу), которая откроет попытку сбой, потому что ваш нераскрытый, непосещенный объект все еще имеет элемент open , Конечно, когда GC собирает элемент (если шаблон Dispose был реализован правильно), ресурс будет освобожден … но вы не знаете, когда это будет, поэтому вы не знаете, откройте этот ресурс. Это основная проблема Dispose работает. Разумеется, освобождение этих ручек часто также освобождает память, и никогда не выпускает их, возможно, никогда не выпустит эту память … следовательно, все разговоры о утечке памяти или задержки в очистке памяти.
Я видел примеры реального мира, вызывающие проблемы. Например, я видел веб-приложения ASP.Net, которые в конечном итоге не могут подключиться к базе данных (хотя и на короткие промежутки времени или до перезапуска процесса веб-сервера), потому что пул соединений SQL Server заполнен »… т.е. , так много соединений были созданы и не были явно выпущены за такой короткий промежуток времени, что никакие новые соединения не могут быть созданы, и многие из подключений в пуле, хотя и неактивны, по-прежнему ссылаются на неоткрытые и непосещенные объекты, t повторно использовать. Правильно удаляя соединения с базой данных, где это необходимо, эта проблема не возникает (по крайней мере, если у вас не очень высокий одновременный доступ).
Если объект реализует IDisposable
, то да, вы должны его утилизировать. Объект может зависеть от собственных ресурсов (дескрипторы файлов, объекты ОС), которые могут быть освобождены немедленно в противном случае. Это может привести к голоданию ресурсов, проблемам блокировки файлов и другим тонким ошибкам, которых в противном случае можно было бы избежать.
См. Также « Реализация метода удаления в MSDN».
Если они реализуют интерфейс IDisposable, вы должны их уничтожить. Сборщик мусора позаботится обо всем остальном.
EDIT: лучше всего using
команду use при работе с одноразовыми элементами:
using(var con = new SqlConnection("..")){ ...
Обычно нет необходимости устанавливать поля в null. Однако я всегда рекомендую избавиться от неуправляемых ресурсов.
Из опыта я также советую вам сделать следующее:
- Отказаться от событий, если они вам больше не нужны.
- Установите любое поле, содержащее делегат, или выражение в значение null, если оно больше не требуется.
Мне очень трудно найти проблемы, которые были прямым результатом того, что вы не следуете приведенным выше советам.
Хорошее место для этого – в Dispose (), но скорее обычно лучше.
В общем случае, если ссылка существует для объекта, сборщик мусора (GC) может занять несколько поколений дольше, чтобы выяснить, что объект больше не используется. Все время объект остается в памяти.
Это может быть не проблема, пока вы не обнаружите, что ваше приложение использует намного больше памяти, чем вы ожидали. Когда это произойдет, подключите профилировщик памяти, чтобы увидеть, какие объекты не очищаются. Установка полей, ссылающихся на другие объекты на нулевые и очищающие коллекции при утилизации, может действительно помочь GC определить, какие объекты он может удалить из памяти. GC вернет используемую память быстрее, делая ваше приложение намного менее голодным и быстрым.
Всегда вызывать dispose. Это не стоит риска. С уважением относиться к большим управляемым корпоративным приложениям. Никаких предположений не может быть сделано, иначе он вернется, чтобы укусить вас.
Не слушайте леппи.
Многие объекты на самом деле не реализуют IDisposable, поэтому вам не нужно беспокоиться о них. Если они действительно выйдут из сферы действия, они будут автоматически освобождены. Также я никогда не сталкивался с ситуацией, когда мне приходилось устанавливать что-то нулевое.
Одна вещь, которая может случиться, состоит в том, что многие объекты могут быть открыты. Это может значительно увеличить использование памяти вашего приложения. Иногда трудно решить, действительно ли это утечка памяти, или ваше приложение просто делает много вещей.
Инструменты профилей памяти могут помочь с такими вещами, но это может быть сложно.
Кроме того, всегда отказаться от подписки на события, которые не нужны. Также будьте осторожны с привязкой WPF и элементами управления. Это не обычная ситуация, но я столкнулся с ситуацией, когда у меня был элемент управления WPF, связанный с базовым объектом. Основной объект был большим и занимал большую часть памяти. Элемент управления WPF заменялся новым экземпляром, и по какой-то причине по-прежнему висел старый. Это вызвало большую утечку памяти.
В hindsite код был плохо написан, но дело в том, что вы хотите удостовериться, что вещи, которые не используются, выходят за frameworks. Это заняло много времени, чтобы найти с профилировщиком памяти, так как трудно понять, какие вещи в памяти действительны, и чего не должно быть.
Когда объект реализует IDisposable
вы должны вызвать Dispose
(или Close
, в некоторых случаях, который вызовет Dispose для вас).
Обычно вам не нужно устанавливать объекты в null
, потому что GC будет знать, что объект больше не будет использоваться.
Есть одно исключение, когда я устанавливаю объекты в null
. Когда я извлекаю много объектов (из базы данных), над которыми мне нужно работать, и сохраняю их в коллекции (или массиве). Когда выполняется «работа», я устанавливаю объект в null
, потому что GC не знает, что я закончил работу с ним.
Пример:
using (var db = GetDatabase()) { // Retrieves array of keys var keys = db.GetRecords(mySelection); for(int i = 0; i < keys.Length; i++) { var record = db.GetRecord(keys[i]); record.DoWork(); keys[i] = null; // GC can dispose of key now // The record had gone out of scope automatically, // and does not need any special treatment } } // end using => db.Dispose is called
Я тоже должен ответить. JIT генерирует таблицы вместе с кодом из его статического анализа использования переменных. Эти записи в таблице являются «GC-Roots» в текущем фрейме стека. По мере продвижения указателя инструкций эти записи в таблице становятся недействительными и поэтому готовы к сбору мусора. Поэтому: если это переменная с областью действия, вам не нужно устанавливать ее в null – GC будет собирать объект. Если это член или статическая переменная, вы должны установить его значение null
В этом эпизоде .NET Rocks есть хорошая дискуссия по этому вопросу (наряду с историей за шаблоном Dispose)!