Должны ли вы установить все объекты null
( Nothing
в VB.NET), как только закончите с ними?
Я понимаю, что в .NET важно избавляться от любых экземпляров объектов, которые реализуют IDisposable
интерфейс, чтобы высвободить некоторые ресурсы, хотя объект все еще может быть чем-то после его удаления (отсюда и isDisposed
свойство в формах), поэтому я предполагаю, что он все еще может находиться в памяти или хотя бы частично?
Я также знаю, что когда объект выходит из области видимости, он помечается для сбора, готового к следующему проходу сборщика мусора (хотя это может занять некоторое время).
Таким образом, имея это в виду, будет ли установка для null
ускорения высвобождения памяти системой, поскольку не нужно выяснять, что она больше не находится в области действия и являются ли они плохими побочными эффектами?
Статьи MSDN никогда не делают этого в примерах, и в настоящее время я делаю это, потому что не вижу вреда. Однако я натолкнулся на смесь мнений, поэтому любые комментарии полезны.
Карл абсолютно прав, нет необходимости устанавливать объекты в null после использования. Если объект реализуется IDisposable
, просто убедитесь, что вы вызываете, IDisposable.Dispose()
когда закончите с этим объектом (обернутым в try
.. finally
или, в using()
блок). Но даже если вы не помните, чтобы вызывать Dispose()
, метод finaliser на объекте должен вызывать Dispose()
вас.
Я думал, что это хорошее лечение
и это
Нет смысла пытаться угадать GC и его стратегии управления, потому что он самонастраивается и непрозрачен. Здесь была хорошая дискуссия о внутренней работе с Джеффри Рихтером в Dot Net Rocks: Джеффри Рихтер о модели памяти Windows и книга Рихтерса CLR через C # глава 20 отлично справились:
Некоторые объекты предполагают .dispose()
метод, который вынуждает ресурс быть удаленным из памяти.
Нет, не обнуляйте объекты. Вы можете проверить http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx для получения дополнительной информации, но настройки вещей к нулю не будет ничего делать, кроме грязного вашего кода.
Единственный раз, когда вы должны установить переменную в null, это когда переменная не выходит из области видимости, и вам больше не нужны связанные с ней данные. Иначе в этом нет необходимости.
Также:
using(SomeObject object = new SomeObject())
{
// do stuff with the object
}
// the object will be disposed of
В некоторых случаях имеет смысл обнулить ссылки. Например, когда вы пишете коллекцию - например, очередь с приоритетами - и по вашему контракту вы не должны оставлять эти объекты живыми для клиента после того, как клиент удалил их из очереди.
Но такого рода вещи имеют значение только в долгоживущих коллекциях. Если очередь не переживет конец функции, в которой она была создана, то это будет иметь гораздо меньшее значение.
В целом, вы действительно не должны беспокоиться. Пусть компилятор и GC выполнят свою работу, чтобы вы могли выполнять свою.
В общем, нет необходимости обнулять объекты после использования, но в некоторых случаях я считаю, что это хорошая практика.
Если объект реализует IDisposable и хранится в поле, я думаю, это хорошо, чтобы обнулить его, просто чтобы избежать использования удаленного объекта. Ошибки следующего вида могут быть болезненными:
this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();
Хорошо обнулить поле после его удаления и получить NullPtrEx прямо в строке, где поле используется снова. В противном случае вы можете столкнуться с какой-то загадочной ошибкой (в зависимости от того, что именно делает DoSomething).
.Dispose()
. Если вы найдете его, вы не используете IDisposable правильно. Единственное использование для одноразового предмета должно быть в пределах блока использования. А после использования блока у вас даже нет доступа к нему myField
. А в блоке using установка значения null
не обязательна, блок using выберет объект для вас. Скорее всего, ваш код недостаточно структурирован, если вам нужны null
переменные.
Есть несколько способов ограничить область действия переменной:
Как упомянуто Стивом Транби
using(SomeObject object = new SomeObject())
{
// do stuff with the object
}
// the object will be disposed of
Точно так же вы можете просто использовать фигурные скобки:
{
// Declare the variable and use it
SomeObject object = new SomeObject()
}
// The variable is no longer available
Я считаю, что использование фигурных скобок без каких-либо «заголовков» действительно очищает код и помогает сделать его более понятным.
Еще одна причина, по которой вы не можете устанавливать объекты в null, когда вы закончите с ними, заключается в том, что они могут поддерживать их живыми дольше.
например
void foo()
{
var someType = new SomeType();
someType.DoSomething();
// someType is now eligible for garbage collection
// ... rest of method not using 'someType' ...
}
позволит объекту, на который ссылается someType, быть GC'd после вызова DoSomething, но
void foo()
{
var someType = new SomeType();
someType.DoSomething();
// someType is NOT eligible for garbage collection yet
// because that variable is used at the end of the method
// ... rest of method not using 'someType' ...
someType = null;
}
иногда может поддерживать объект в живых до конца метода. JIT обычно оптимизируется прочь присваивание нуля , так как биты конца кода вверх является одинаковым.
GC.KeepAlive(someType);
См. Ericlippert.com/2013/06/10/construction-destructionВзгляните также на эту статью: http://www.codeproject.com/KB/cs/idisposable.aspx
По большей части установка нулевого объекта не имеет никакого эффекта. Единственный раз, когда вы должны быть уверены, это если вы работаете с «большим объектом», размер которого превышает 84 КБ (например, растровые изображения).
этот вид «нет необходимости устанавливать объекты равными нулю после использования» не совсем точен. Есть моменты, когда вам нужно обнулять переменную после ее удаления.
Да, вы должны ВСЕГДА звонить .Dispose()
или .Close()
на все, что есть, когда вы закончите. Будь то файловые дескрипторы, соединения с базой данных или одноразовые объекты.
Отдельно от этого очень практичный шаблон LazyLoad.
Скажем , у меня есть и экземпляры ObjA
из class A
. Class A
имеет общественное свойство PropB
из class B
.
Внутренне PropB
использует закрытую переменную _B
и по умолчанию имеет значение null. Когда PropB.Get()
используется, он проверяет, _PropB
является ли значение null, и если это так, открывает ресурсы, необходимые для создания экземпляра B
в _PropB
. Затем он возвращается _PropB
.
По моему опыту, это действительно полезный трюк.
Когда возникает необходимость в нулевом _PropB
значении A
, если вы сбрасываете или изменяете A таким образом, чтобы содержимое было дочерним по отношению к предыдущим значениям , вам нужно будет _PropB
избавиться от AND, чтобы исключить нулевое значение, чтобы LazyLoad мог сбросить для получения правильного значения, ЕСЛИ код требует этого.
Если вы только _PropB.Dispose()
и вскоре после этого ожидаете, что проверка LazyLoad на ноль будет успешной, она не будет нулевой, и вы будете просматривать устаревшие данные. По сути, вы должны обнулить его, Dispose()
чтобы быть уверенным.
Я уверен , что бы это было иначе, но у меня есть код прямо сейчас , проявляющий это поведение после того, как Dispose()
на _PropB
и за пределами вызывающей функции , что сделал в Dispose (и , таким образом , почти из области видимости), частный проп еще не равно нулю, и устаревшие данные все еще там.
В конце концов, свойство disposed будет недействительным, но это было недетерминировано с моей точки зрения.
Основная причина, на которую ссылается dbkk, заключается в том, что родительский контейнер ( ObjA
с PropB
) сохраняет экземпляр _PropB
в области видимости, несмотря на Dispose()
.
В общем случае нет необходимости устанавливать значение NULL. Но предположим, что у вас есть функция сброса в вашем классе.
Тогда вы можете это сделать, потому что вы не хотите вызывать dispose дважды, поскольку некоторые из Dispose могут быть реализованы неправильно и вызывать исключение System.ObjectDisposed.
private void Reset()
{
if(_dataset != null)
{
_dataset.Dispose();
_dataset = null;
}
//..More such member variables like oracle connection etc. _oraConnection
}
Я верю, что разработчики GC не смогут ускорить GC с помощью аннулирования. Я уверен, что они предпочли бы, чтобы вы не беспокоились о том, как / когда GC бежит - относитесь к этому как к вездесущему существу, которое защищает и наблюдает за вами ... (склоняет голову, поднимает кулак в небо) .. ,
Лично я часто в явном виде устанавливаю переменные в null, когда заканчиваю с ними в качестве самостоятельной документации. Я не объявляю, использую, затем устанавливаю на нуль позже - я обнуляю сразу после того, как они больше не нужны. Я говорю прямо: «Я официально закончил с тобой ... уйди ...»
Нужно ли обнулять в языке GC'd? Нет. Это полезно для GC? Может быть, да, может, нет, не знаю наверняка, по своей конструкции я действительно не могу это контролировать, и независимо от сегодняшнего ответа с этой версией или той, будущие реализации GC могут изменить ответ вне моего контроля. Плюс, если / когда обнуление оптимизировано, это немного больше, чем модный комментарий, если хотите.
Я полагаю, если это прояснит мои намерения следующему бедному дураку, который пойдет по моим стопам, и если это «может» потенциально помочь ГК, то оно того стоит для меня. В основном это заставляет меня чувствовать себя опрятным и ясным, а Монго любит чувствовать себя опрятным и чистым. :)
Я смотрю на это так: языки программирования существуют, чтобы люди могли дать другим людям представление о намерениях, а компилятор запросил задание о том, что делать - компилятор преобразует этот запрос в другой язык (иногда несколько) для CPU - Процессор (ы) может дать подсказку, какой язык вы используете, настройки вкладок, комментарии, стилистические акценты, имена переменных и т. д. - Процессор - это все о битовом потоке, который сообщает ему, какие регистры и коды операций и места в памяти можно менять. Многие вещи, написанные в коде, не преобразуются в то, что потребляется процессором в указанной нами последовательности. Наш C, C ++, C #, Lisp, Babel, ассемблер или что-то еще является теорией, а не реальностью, написанной как формулировка работы. То, что вы видите, не то, что вы получаете, да, даже на языке ассемблера.
Я понимаю, что «ненужные вещи» (например, пустые строки) «не что иное, как шум и загромождение кода». Это был я ранее в моей карьере; Я полностью понимаю это. На этом этапе я склоняюсь к тому, что делает код более понятным. Это не то, что я добавляю даже 50 строк "шума" в мои программы - это несколько строк здесь или там.
Есть исключения из любого правила. В сценариях с энергозависимой памятью, статической памятью, состояниями гонки, одиночными событиями, использованием «устаревших» данных и всем этим видом гнили все по-другому: вам НУЖНО управлять собственной памятью, блокируя и обнуляя как, кстати, потому что память не является частью Вселенная GC'd - надеюсь, все это понимают. В остальное время с языками GC это вопрос стиля, а не необходимости или гарантированного повышения производительности.
В конце дня убедитесь, что вы понимаете, что подходит для GC, а что нет; блокировать, распоряжаться и обнулять соответственно; воск, воск от; вдох-выдох; и для всего остального я говорю: если это хорошо, сделай это. Ваш пробег может варьироваться ... как следует ...
Стивен Клири очень хорошо объясняет в этом посте: Должен ли я установить переменные в Null, чтобы помочь в сборке мусора?
Говорит:
Краткий ответ для Нетерпеливого Да, если переменная является статическим полем, или если вы пишете перечислимый метод (с использованием yield return) или асинхронный метод (с использованием async и await). В противном случае нет.
Это означает, что в обычных методах (не перечисляемых и не асинхронных) локальные переменные, параметры метода или поля экземпляра не устанавливаются равными нулю.
(Даже если вы реализуете IDisposable.Dispose, вы все равно не должны устанавливать переменные в null).
Важной вещью, которую мы должны рассмотреть, являются Статические поля .
Статические поля всегда являются корневыми объектами , поэтому сборщик мусора всегда считает их «живыми» . Если статическое поле ссылается на объект, который больше не нужен, ему следует установить значение null, чтобы сборщик мусора рассматривал его как подходящий для сбора.
Установка статических полей в ноль не имеет смысла, если весь процесс завершается. На этом этапе собирается куча мусора, включая все корневые объекты.
Заключение:
Статические поля ; это об этом. Все остальное - пустая трата времени .
Я думаю, что установка чего-либо на ноль - это грязно. Представьте себе сценарий, в котором элемент, который будет установлен в данный момент, выставляется, скажем, через свойство. Теперь, когда какой-то фрагмент кода случайно использует это свойство после удаления элемента, вы получите исключение нулевой ссылки, которое требует некоторого исследования, чтобы точно выяснить, что происходит.
Я полагаю, что одноразовые рамки позволят создать исключение ObjectDisposedException, которое является более значимым. По этой причине лучше не устанавливать их обратно в ноль.