Установка объектов в значение Null/Nothing после использования в .NET

StackOverflow https://stackoverflow.com/questions/2785

Вопрос

Если вы установите для всех объектов null (Nothing в VB.NET), как только вы с ними закончите?

Я понимаю, что в .NET важно удалять любые экземпляры объектов, реализующих IDisposable интерфейс для освобождения некоторых ресурсов, хотя объект все еще может быть чем-то после его удаления (следовательно, isDisposed свойство в формах), поэтому я предполагаю, что оно все еще может находиться в памяти или, по крайней мере, частично?

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

Итак, имея это в виду, установим его на null ускорить освобождение памяти системой, поскольку ей не обязательно выяснять, что она больше не входит в область действия, и есть ли какие-либо плохие побочные эффекты?

Статьи MSDN никогда не делают этого в примерах, и в настоящее время я делаю это, поскольку я не вижу вреда.Однако я столкнулся с разными мнениями, поэтому любые комментарии полезны.

Это было полезно?

Решение

Карл абсолютно прав, нет необходимости присваивать объектам значение null после использования.Если объект реализует IDisposable, просто обязательно позвони IDisposable.Dispose() когда вы закончите работу с этим объектом (завернутым в try..finally, или, using() блокировать).Но даже если ты не забудешь позвонить Dispose(), метод финализатора объекта должен вызывать Dispose() для тебя.

Я подумал, что это хорошее лечение:

Копаем в IDisposable

и это

Понимание IDisposable

Нет никакого смысла пытаться пересмотреть GC и его стратегии управления, поскольку они самонастраиваются и непрозрачны.Здесь была хорошая дискуссия о внутренней работе с Джеффри Рихтером на Dot Net Rocks: Джеффри Рихтер о модели памяти Windows и книга Рихтерса CLR через C# В главе 20 есть отличная трактовка:

Другие советы

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

например

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is now eligible for garbage collection         

    // ... rest of method not using 'someType' ...
}

позволит объекту, на который ссылается someType, быть GC после вызова «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 обычно оптимизирует присвоение значения null., поэтому оба фрагмента кода оказываются одинаковыми.

Нет, не обнуляйте объекты.Вы можете проверить 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

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

Если объект реализует IDisposable и хранится в поле, я думаю, что лучше обнулить его, просто чтобы избежать использования удаленного объекта.Ошибки следующего типа могут быть болезненными:

this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();

Хорошо обнулить поле после его удаления и получить NullPtrEx прямо в той строке, где поле используется снова.В противном случае вы можете столкнуться с какой-нибудь загадочной ошибкой в ​​дальнейшем (в зависимости от того, что именно делает DoSomething).

Скорее всего, ваш код недостаточно структурирован, если вы чувствуете необходимость 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, — это когда переменная не выходит за пределы области действия и вам больше не нужны связанные с ней данные.В противном случае нет необходимости.

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

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

private void Reset()
{
    if(_dataset != null)
    {
       _dataset.Dispose();
       _dataset = null;
    }
    //..More such member variables like oracle connection etc. _oraConnection
 }

такого рода фразы «нет необходимости присваивать объектам значение null после использования» не совсем точны.Бывают случаи, когда вам нужно обнулить переменную после ее удаления.

Да, вам следует ВСЕГДА звонить .Dispose() или .Close() на всем, что у него есть, когда вы закончите.Будь то дескрипторы файлов, подключения к базе данных или одноразовые объекты.

Отдельно стоит отметить очень практичный шаблон LazyLoad.

Скажем, у меня есть и создан экземпляр ObjA из class A. Class A имеет публичную собственность под названием PropB из class B.

Внутренне, PropB использует частную переменную _B и по умолчанию равен нулю.Когда PropB.Get() используется, он проверяет, есть ли _PropB имеет значение NULL, и если это так, открывает ресурсы, необходимые для создания экземпляра B в _PropB.Затем он возвращается _PropB.

По моему опыту, это действительно полезный трюк.

Необходимость обнуления возникает, если вы сбрасываете или изменяете A каким-либо образом, что содержимое _PropB были дочерними элементами предыдущих значений A, вам нужно будет удалить И обнулить _PropB поэтому LazyLoad может выполнить сброс для получения правильного значения, ЕСЛИ этого требует код.

Если ты делаешь только _PropB.Dispose() и вскоре после того, как ожидается, что проверка на нулевое значение для LazyLoad завершится успешно, оно не будет нулевым, и вы увидите устаревшие данные.По сути, вы должны обнулить его после Dispose() просто чтобы быть уверенным.

Я бы, конечно, хотел, чтобы все было иначе, но сейчас у меня есть код, демонстрирующий такое поведение после Dispose() на _PropB и за пределами вызывающей функции, которая выполнила Dispose (и, таким образом, почти выходит за рамки), приватное свойство все еще не равно нулю, и устаревшие данные все еще существуют.

В конце концов, выброшенное свойство обнулится, но с моей точки зрения это недетерминировано.

Основная причина, как указывает dbkk, заключается в том, что родительский контейнер (ObjA с PropB) сохраняет экземпляр _PropB по объему, несмотря на Dispose().

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

Но подобные вещи имеют значение только в долгоживущих коллекциях.Если очередь не переживет завершение функции, в которой она была создана, то это имеет гораздо меньшее значение.

В целом, вам действительно не стоит заморачиваться.Позвольте компилятору и сборщику мусора делать свою работу, а вы можете заняться своей.

Взгляните также на эту статью: http://www.codeproject.com/KB/cs/idisposable.aspx

По большей части установка объекта в значение null не имеет никакого эффекта.Единственный раз, когда вам следует это сделать, — это если вы работаете с «большим объектом», размер которого превышает 84 КБ (например, растровые изображения).

Стивен Клири очень хорошо объясняет в этом посте: Должен ли я установить для переменных значение NULL, чтобы облегчить сбор мусора?

Говорит:

Краткий ответ, для нетерпеливого да, если переменная является статическим полем, или если вы пишете перечисляемый метод (используя возврат доходности) или асинхронный метод (используя асинхромы и ожидается).В противном случае нет.

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

(Даже если вы реализуете IDisposable.Dispose, вам все равно не следует устанавливать для переменных значение null).

Важная вещь, которую мы должны учитывать, это Статические поля.

Статические поля всегда являются корневыми объектами., так они есть всегда считался «живым» у сборщика мусора.Если статическое поле ссылается на объект, который больше не нужен, ему следует присвоить значение null, чтобы сборщик мусора считал его пригодным для сбора.

Установка статических полей в значение null бессмысленна, если весь процесс завершается.В этот момент вся куча, включая все корневые объекты, будет очищена от мусора.

Заключение:

Статические поля;вот и все.Все остальное является пустая трата времени.

Я считаю, что по замыслу разработчиков GC вы не можете ускориться GC с обнулением.Я уверен, что они предпочли бы, чтобы вы не беспокоились о том, как и когда запускается GC - относитесь к этому повсеместно. Существование защищая и наблюдая за тобой... (наклоняет голову, поднимает кулак к небу)...

Лично я часто явно присваиваю переменным значение null, когда заканчиваю работу с ними, в качестве формы самодокументирования.Я не объявляю, не использую, а затем позже устанавливаю значение null — я обнуляю значение сразу после того, как они больше не нужны.Я прямо говорю: «Официально я с тобой покончил… уходи…»

Необходимо ли обнуление на языке GC?Нет.Полезно ли это для GC?Может быть, да, может быть, нет, не знаю наверняка, по замыслу я действительно не могу это контролировать, и независимо от сегодняшнего ответа с той или иной версией, будущие реализации GC могут изменить ответ вне моего контроля.Плюс, если/когда обнуление оптимизировано, это не более чем фантастика комментарий если вы будете.

Я думаю, если это прояснит мои намерения следующему бедному дураку, который пойдет по моим стопам, и если это "мощь" потенциально может иногда помочь GC, тогда для меня оно того стоит.В основном это заставляет меня чувствовать себя опрятно и ясно, а Монго нравится чувствовать себя опрятно и ясно.:)

Я смотрю на это так:Языки программирования существуют для того, чтобы люди могли дать другим людям представление о намерениях, а компилятору — запрос на работу о том, что делать — компилятор преобразует этот запрос в другой язык (иногда в несколько) для ЦП — ЦП (ЦП) могут дать неважно, какой язык вы использовали, настройки вкладок, комментарии, стилистические акценты, имена переменных и т. д.- все дело в битовом потоке процессора, который сообщает ему, какие регистры, коды операций и ячейки памяти нужно менять.Многие вещи, написанные в коде, не преобразуются в то, что потребляется ЦП в указанной нами последовательности.Наши C, C++, C#, Lisp, Babel, ассемблер или что-то еще — это теория, а не реальность, написанная как техническое задание.То, что вы видите, — это не то, что вы получаете, да, даже на языке ассемблера.

Я понимаю мышление «ненужных вещей» (например, пустые строки) « - это не что иное, как шум и загромождать код». Это был я в начале моей карьеры;Я это полностью понимаю.На данном этапе я склоняюсь к тому, что делает код более понятным.Я не добавляю в свои программы даже 50 строк «шума» — это несколько строк здесь или там.

Из любого правила есть исключения.В сценариях с энергозависимой памятью, статической памятью, состояниями гонки, синглтонами, использованием «устаревших» данных и всей подобной гнилью все по-другому:вам НУЖНО управлять своей собственной памятью, блокируя и обнуляя ее, поскольку память не является частью вселенной GC - надеюсь, все это понимают.В остальное время с языками GC это вопрос стиля, а не необходимости или гарантированного повышения производительности.

В конце концов, убедитесь, что вы понимаете, что подходит для GC, а что нет;блокировать, удалять и аннулировать соответствующим образом;наносить воск, снимать воск;вдох-выдох;а обо всем остальном я говорю:Если вам приятно, сделайте это.Ваш пробег может варьироваться... как и должно быть...

Какой-то объект предполагает, что .dispose() метод, который принудительно удаляет ресурс из памяти.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top