Как Garbage Collector в Java справляется с циклическими ссылками?

«Как Garbage Collector в Java справляется с циклическими ссылками?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Современные сборщики мусора в Java (на основе алгоритма mark-and-sweep и его производных) корректно удаляют объекты с циклическими зависимостями, если вся группа объектов стала недостижимой из GC Roots.

Принцип работы:

  1. На фазе Mark GC проходит по всем ссылкам, начиная с корней.
  2. Если группа объектов ссылается друг на друга, но ни на один из них нет ссылки извне этой группы (от корня), ни один объект в цикле не будет помечен как живой.
  3. На фазе Sweep вся циклическая структура удаляется.

Пример циклической ссылки:

class Node {
    Node neighbor;
}

Node a = new Node();
Node b = new Node();
a.neighbor = b; // a ссылается на b
b.neighbor = a; // b ссылается на a (цикл)
// Ключевой момент:
a = null;
b = null;
// Теперь на внутренний цикл (a<->b) нет внешних ссылок.
// Оба объекта недостижимы и будут удалены GC.

Важно: Для корректной работы GC не требуется специальной обработки циклов. Проблема "циклических ссылок" актуальна для счетчиков ссылок (как в C++), но не для tracing-сборщиков (как в Java), которые отслеживают достижимость от корней.