Сборщик мусора в java - установить объект null

Предположим, что существует объект Tree с корневым объектом TreeNode, и каждый TreeNode имеет leftNode и rightNode объекты (например, объект BinaryTree)

Если я звоню:

myTree = null;

что действительно происходит с связанными объектами TreeNode внутри дерева? Будет ли собран мусор, или я должен установить null все связанные объекты внутри объекта дерева?

Ответ 1

Сбор мусора в Java выполняется на основе "достижимости". JLS определяет этот термин следующим образом:

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

Пока объект доступен * он не имеет права на сбор мусора.

JLS отказывается от реализации Java, чтобы выяснить, как определить, доступен ли объект. Если реализация не может быть уверенной, она свободна рассматривать теоретически недостижимый объект как достижимый... и не собирать его. (Действительно, JLS позволяет реализовать не собирать ничего, никогда! Никакая разумная реализация не сделает этого.)

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


Вот что это значит для вашего вопроса:

Если я вызываю: myTree = null; что действительно происходит с связанными объектами TreeNode внутри дерева? Будет ли собран мусор, или я должен установить null все связанные объекты внутри объекта дерева?

Предположим, что myTree содержит последнюю оставшуюся доступную ссылку на корень дерева.

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

Если myTree не содержит последней доступной ссылки на корень дерева, то обнуление внутренней ссылки является ошибкой по той же причине, что и в 3. выше.


Итак, когда вы должны null вещи, чтобы помочь сборщику мусора?

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

  • Ссылки на объекты в переменных класса... которые (по определению) никогда не выходят за рамки.
  • Ссылки на объекты в локальных переменных, которые все еще находятся в области... но не будут использоваться. Например:

     public List<Pig> pigSquadron(boolean pigsMightFly) {
       List<Pig> airbornePigs = new ArrayList<Pig>();
       while (...) {
         Pig piggy = new Pig();
         ...
         if (pigsMightFly) {
           airbornePigs.add(piggy);
         }
         ...
       }
       return airbornePigs.size() > 0 ? airbornePigs : null;
     }
    

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

  • Ссылки на объекты в переменных экземпляра или в ячейках массива, где инварианты структуры данных означают, что они не будут использоваться. Пример примера @edalorzo - пример этого.

Следует отметить, что компилятор/среда выполнения иногда могут определить, что переменная в области видимости эффективно мертва. Например:

public void method(...) {
    Object o = ...
    Object p = ...
    while (...) {
        // Do things to 'o' and 'p'
    }
    // No further references to 'o'
    // Do lots more things to 'p'
}

Некоторые компиляторы Java/runtimes могут обнаружить, что "o" не требуется после окончания цикла и обрабатывает переменную как мертвую.


* Фактически, речь идет о достижении strong. Модель достижимости GC более сложна, если вы рассматриваете мягкие, слабые и phantom ссылки. Однако они не имеют отношения к случаю использования OP.

Ответ 2

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

Чтобы сборщик мусора удалял объект myTree, просто сделайте вызов gc() после того, как вы установите его на null

myTree=null;
System.gc();

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

Ответ 3

Они будут собирать мусор, если у вас нет других ссылок на них (возможно, вручную). Если у вас есть только ссылка на дерево, тогда да, они будут собирать мусор.

Ответ 4

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

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

Ответ 5

В Java вам не нужно явно устанавливать объекты на null, чтобы они были GC'd. Объекты имеют право на GC, когда нет ссылок на него (игнорируя классы java.lang.ref.*).

Ответ 6

Объект собирается, если на нем больше нет ссылок.

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

Это, конечно, не так, если у вас есть выдающиеся ссылки на узлы за пределами дерева. Те получат GC'd, как только эти ссылки выйдут из сферы действия (вместе с чем-то, на что они ссылаются)