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

167

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

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

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

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

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

  • 4
    +1 отличный вопрос. Кто-нибудь знает обстоятельства, при которых компилятор полностью оптимизирует назначение? то есть кто-нибудь смотрел на MSIL при других обстоятельствах и отмечал IL для установки объекта на ноль (или его отсутствие).
Теги:
memory
memory-management

13 ответов

67
Лучший ответ

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

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

Копаем в IDisposable

и этот

Понимание IDisposable

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

  • 6
    Правило о том, что не следует устанавливать значение NULL, не является «жестким и быстрым» ... если объект помещается в кучу больших объектов (размер> 85 КБ), это поможет ГХ, если вы установите для объекта значение NULL, когда вы закончите. используй это.
  • 0
    Я согласен в ограниченной степени, но если только вы не начинаете испытывать нехватку памяти, я не вижу необходимости «преждевременной оптимизации», устанавливая нулевые объекты после использования.
Показать ещё 6 комментариев
34

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

например.

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

    // ... rest of method not using '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 обычно оптимизирует присвоение нулевому значению, поэтому оба бита кода оказываются одинаковыми.

  • 0
    Это интересный момент. Я всегда думал, что объекты не выходят за рамки, пока не завершится метод, в котором они определены. Если, конечно, объект не находится в пределах блока Using или явно установлен в Nothing или null.
  • 1
    Предпочтительный способ гарантировать, что они остаются живыми, это использовать GC.KeepAlive(someType); См. Ericlippert.com/2013/06/10/construction-destruction
15

Нет нулевых объектов. Вы можете проверить http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx для получения дополнительной информации, но установление нулевого значения ничего не сделает, кроме грязного кода.

  • 1
    Приятное и подробное объяснение памяти в общей ссылке
  • 0
    Ссылка не работает. Без связанного контента этот ответ скорее бесполезен и должен быть удален.
7

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

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

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

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

  • 8
    Хорошо, удаленный объект должен вызвать исключение ObjectDisposedException, если он уже был удален. Насколько мне известно, для этого требуется повсеместный шаблонный код, но опять же, Disposed в любом случае является плохо продуманной парадигмой.
  • 3
    Ctrl + F для .Dispose() . Если вы найдете его, вы не используете IDisposable правильно. Единственное использование для одноразового предмета должно быть в пределах блока использования. А после использования блока у вас даже нет доступа к myField . А в блоке using установка в null не обязательна, блок using избавится от объекта за вас.
7

также:

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of
6

Скорее всего, ваш код недостаточно структурирован, если вы чувствуете потребность в переменных null.

Существует несколько способов ограничить область действия переменной:

Как упоминалось Steve Tranby

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

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

  • 0
    Однажды я попытался использовать пользовательские локальные области видимости (в основном это смартта $$). Компания взорвалась.
  • 0
    Еще одно замечание: это потому, что компилятор c # найдет переменные локальной области, которые реализуют IDisposable, и вызовет .Dispose (MOST Of the time), когда закончится их область действия. Однако ... SQL-соединения - это одно большое время, когда .Dispose () никогда не оптимизируется. Есть некоторые типы, которые требуют явного внимания, поэтому я лично всегда делаю вещи явно, чтобы меня не укусили.
5

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

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

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

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

  • 2
    Это правда, но это также означает, что вы, вероятно, должны провести рефакторинг своего кода. Я не думаю, что мне когда-либо нужно было объявлять переменную вне ее предполагаемой области видимости.
  • 2
    Если понимать, что «переменная» включает в себя поля объекта, то этот ответ имеет большой смысл. В случае, когда «переменная» означает только «локальная переменная» (метода), тогда мы, вероятно, говорим здесь о нишевых случаях (например, метод, который выполняется намного дольше, чем обычно).
3

этот тип "нет необходимости устанавливать объекты в null после использования" не совсем точен. Иногда вам нужно NULL переменную после ее утилизации.

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

Отдельно от этого очень практичный образец LazyLoad.

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

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

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

Если требуется нулевое значение, это если вы reset или изменили A каким-то образом, что содержимое _PropB было дочерним по отношению к предыдущим значениям A, вам нужно будет Dispose AND null out _PropB поэтому LazyLoad может reset получить правильное значение, если код требует его.

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

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

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

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

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

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

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

1

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

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

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

0

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

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

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

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

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

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

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

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

-2

Некоторые объекты предполагают метод .dispose(), который заставляет ресурс удаляться из памяти.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню