Влияет ли присвоение объектам значения null в Java сборку мусора?

85

Улучшает ли присвоение неиспользуемой ссылки на объект nullв Java процесс сборки мусора каким-либо измеримым образом?

Мой опыт работы с Java (и C #) научил меня, что попытки перехитрить виртуальную машину или JIT-компилятор часто противоречат интуиции, но я видел, как коллеги использовали этот метод, и мне любопытно, стоит ли это выбирать. вверх или одно из тех суеверий программирования вуду?

Джеймс МакМахон
источник

Ответы:

66

Обычно нет.

Но как и все: зависит от обстоятельств. GC в Java в наши дни ОЧЕНЬ хорош, и все должно быть очищено очень скоро после того, как он станет недоступным. Это происходит сразу после того, как метод оставлен для локальных переменных, и когда на экземпляр класса больше не ссылаются для полей.

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

Например, этот код из ArrayList:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

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

И то и другое:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

и:

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

Функционально эквивалентны.

Марк Ренуф
источник
7
вы также можете захотеть обнулить ссылки внутри методов, которые могут использовать большой объем памяти или занимать много времени для выполнения, или в коде приложения Swing, которое может работать долгое время. В коротких методах или короткоживущих объектах не стоит лишнего запутанного кода.
Джон Гарднер
1
Так правильно ли, когда мы обнуляем объект, сборщик мусора немедленно собирает этот объект ??
Мухаммад Бабар
1
Нет. Сборщик мусора периодически запускается для проверки наличия объектов, на которые не указывают ссылочные переменные. Когда вы установите для них значение null, а затем вызовете, System.gc()он будет удален (при условии, что нет других ссылок, указывающих на этот объект).
JavaTechnical 05
2
@JavaTechnical Насколько я знаю, запуск сборки мусора при вызове System.gc () не гарантируется.
Джек
12

По моему опыту, чаще всего люди игнорируют ссылки из-за паранойи, а не по необходимости. Вот краткое руководство:

  1. Если объект A ссылается на объект B, и вам больше не нужна эта ссылка, а объект A не имеет права на сборку мусора, вы должны явно обнулить поле. Нет необходимости обнулять поле, если охватывающий объект все равно собирает мусор. Обнуление полей в методе dispose () почти всегда бесполезно.

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

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

Gili
источник
11

Хорошая статья - сегодняшний ужас кодирования .

Работа GC заключается в поиске объектов, на которые нет указателей, область их поиска - это куча / стек и любые другие пространства, которые у них есть. Поэтому, если вы установите для переменной значение null, фактический объект теперь никем не указывается и, следовательно, может быть GC'd.

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

Проблема также может быть усложнена оптимизацией кода: если вы никогда не используете переменную после того, как установили для нее значение null, было бы безопасной оптимизацией удалить строку, которая устанавливает значение null (на одну инструкцию для выполнения меньше). Так что на самом деле вы можете не получить никаких улучшений.

В общем, да, это может помочь, но не будет детерминированным .

EarlNameless
источник
Хороший момент для безопасной оптимизации, которая удаляет строку, устанавливающую для ref значение null.
asgs 08
8

По крайней мере, в java это вообще не вуду-программирование. Когда вы создаете объект в java, используя что-то вроде

Foo bar = new Foo();

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

bar = null ;

и, предполагая, что ничто другое не имеет ссылки на объект, он освобождается и доступен для gc в следующий раз, когда сборщик мусора пройдет мимо.

Чарли Мартин
источник
12
Но выход за пределы области действия приведет к тому же результату без дополнительной строки кода. Если это локально для метода, в этом нет необходимости. Как только вы выйдете из метода, объект будет иметь право на сборку мусора.
duffymo
2
Хорошо. Теперь, для популярной викторины, создайте пример кода, для которого этого НЕ достаточно. Подсказка: они существуют.
Чарли Мартин
7

Это зависит.

Вообще говоря, чем короче вы храните ссылки на свои объекты, тем быстрее они будут собраны.

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

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

Любоша хаско
источник
6

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

Обычно установка ссылок на null означает для ЧИТАТЕЛЯ кода, что этот объект полностью завершен и не должен больше беспокоиться.

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

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

это позволяет сборщику мусора bigThing сразу после выхода из вложенных фигурных скобок.

Торбьёрн Равн Андерсен
источник
Хорошая альтернатива. Это позволяет избежать явной установки переменной в значение NULL и предупреждения некоторых IDE о том, что присвоение NULL не используется.
jk7
5
public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

Выше программу кидает OutOfMemoryError. Если раскомментировать data = null;, проблема OutOfMemoryErrorрешена. Всегда рекомендуется устанавливать для неиспользуемой переменной значение null.

омиэль
источник
Это, imo, солидный набег в мир аргументов соломенного человека. Просто потому, что вы МОЖЕТЕ создать ситуацию, когда установка переменной на null решит проблему в этом конкретном случае (и я не уверен, что вы это сделали), не означает, что установка значения на null является хорошей идеей в каждом случае. Добавление в код для установки каждой переменной, которую вы больше не используете, равной нулю, даже если вскоре после этого они выйдут из области видимости, просто увеличивает раздувание кода и делает код менее удобным в обслуживании.
RHSeeger
Почему не собирается мусор массива byte [] в момент, когда данные переменной выходят за рамки?
Grav,
2
@Grav: выход за пределы области действия не гарантирует, что сборщик мусора соберет локальные объекты. Однако установка для него значения null предотвращает исключение OutOfMemory, потому что вам гарантируется, что сборщик мусора будет запущен до того, как прибегнет к выбросу исключения OutOfMemory, и если для объекта установлено значение null, он будет собираться.
Stefanos T.
См. Также stackoverflow.com/questions/31260594/…
Raedwald 09
4

Однажды я работал над приложением для видеоконференцсвязи и заметил огромную разницу в производительности, когда я потратил время на нулевые ссылки, как только объект мне больше не нужен. Это было в 2003-2004 годах, и я могу только представить, что GC с тех пор стал еще умнее. В моем случае каждую секунду сотни объектов приходили и уходили из области видимости, поэтому я заметил сборщик мусора, когда он периодически срабатывал. Однако после того, как я указал на нулевые объекты, сборщик мусора прекратил приостанавливать мое приложение.

Так что это зависит от того, что вы делаете ...

Перри
источник
2

Да.

Из «Программиста-прагматика» с.292:

Установив ссылку на NULL, вы уменьшите количество указателей на объект на один ... (что позволит сборщику мусора удалить его)


источник
Я думаю, это применимо только в случае алгоритма подсчета ссылок. используется или иначе тоже?
Nrj
3
Этот совет устарел для любого современного сборщика мусора; а сборщик мусора с подсчетом ссылок имеет серьезные проблемы, такие как циклические ссылки.
Лоуренс Дол
Это также было бы верно для GC mark and sweep; наверное, во всех остальных. Тот факт, что у вас на одну ссылку меньше, не означает, что она засчитана. Ответ tweakt объясняет это. Лучше уменьшить область действия, чем устанавливать значение NULL.
1

Я предполагаю, что OP имеет в виду такие вещи:

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

В этом случае, не будет ли виртуальная машина пометить их для GC, как только они покинут область видимости?

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

CodingWithSpike
источник
Время, в которое запускается сборщик мусора, не является детерминированным. Я вообще не верю, что установка объекта nullвлияет на поведение сборщика мусора.
harto,
0

" Это зависит "

Я не знаю о Java, но в .net (C #, VB.net ...) обычно не требуется назначать нуль, когда вам больше не нужен объект.

Однако учтите, что это «обычно не требуется».

Анализируя ваш код, компилятор .net делает хорошую оценку времени жизни переменной ... чтобы точно сказать, когда объект больше не используется. Поэтому, если вы напишете obj = null, это может выглядеть так, как будто объект все еще используется ... в этом случае назначать null контрпродуктивно.

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

Для этого нет жестких правил. Пройдя по указанному выше месту в вашем коде, запустите профилировщик, чтобы узнать, помогает ли он каким-либо образом. Скорее всего, вы не увидите выгоды.

Если вы пытаетесь оптимизировать код .net, то, по моему опыту, тщательная осторожность с методами Dispose и Finalize на самом деле более полезна, чем забота о нулях.

Некоторые ссылки по теме:

http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx

Сеш
источник
0

Даже если бы обнуление ссылки было несколько более эффективным, стоило бы уродства засыпать свой код этими уродливыми обнулениями? Они будут только мешать и скрывать код намерения, который их содержит.

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

Сгарган
источник
0

В будущем выполнении вашей программы значения некоторых элементов данных будут использоваться для компьютерного вывода, видимого вне программы. Другие могут использоваться или не использоваться, в зависимости от будущих (и невозможно предсказать) входов в программу. Другие члены данных могут гарантированно не использоваться. Все ресурсы, включая память, выделенные этим неиспользуемым данным, тратятся впустую. Работа сборщика мусора (GC) состоит в том, чтобы устранить эту потерянную память. Для GC было бы катастрофой устранить то, что было необходимо, поэтому используемый алгоритм мог бы быть консервативным, сохраняя больше, чем строгий минимум. Он может использовать эвристические оптимизации для повышения своей скорости за счет сохранения некоторых элементов, которые на самом деле не нужны. GC может использовать множество потенциальных алгоритмов. Поэтому возможно, что изменения, внесенные в вашу программу, и которые не влияют на правильность вашей программы, тем не менее могут повлиять на работу GC, либо заставляя его работать быстрее, чтобы выполнять ту же работу, либо быстрее определять неиспользуемые элементы. Итак, это изменение, устанавливающее ссылку на объект undd наnull, по идее не всегда вуду.

Это вуду? Сообщается, что для этого есть части кода библиотеки Java. Авторы этого кода намного лучше, чем средние программисты, и либо знают, либо сотрудничают с программистами, которые знают детали реализации сборщика мусора. Так что позволяет предположить , что это иногда польза.

Raedwald
источник
0

Как вы сказали, есть оптимизации, т.е. JVM знает место, когда переменная использовалась в последний раз, и объект, на который она ссылается, может быть GCed сразу после этой последней точки (все еще выполняется в текущей области). Так что обнуление ссылок в большинстве случаев не помогает GC.

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

Это «патология», потому что ЛЮБОЙ продвинутый узел приведет к продвижению ВСЕХ следующих узлов, пока сборщик мусора не решит проблему.

Чтобы избежать кумовства, неплохо обнулить ссылки на объект, который предполагается удалить. Вы можете увидеть, как этот метод применяется в классах JDK: LinkedList и LinkedHashMap.

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}
Кирилл
источник