Как garbage collector понимает, что объект не нужен?

Garbage Collector (GC) в Java определяет, что объект не нужен (т.е. может быть удалён), на основе его достижимости. Если объект не достижим от активных ссылок в программе, он считается "мусором" и подлежит удалению.

📌 Ключевой принцип — достижимость (reachability)

Объект считается живым, если:

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

  • Он доступен от корневых ссылок (GC roots).

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

🔗 Что такое GC roots?

Это начальные точки, от которых начинается обход объектов. К ним относятся:

  • Локальные переменные текущих методов (в стеке)

  • Аргументы методов

  • Ссылки из статических полей (static)

  • Ссылки из активных потоков (Thread)

  • Объекты JNI (native-код)

🔄 Как работает определение "ненужного" объекта?

  1. GC запускает обход от GC roots.

  2. Каждому достижимому объекту ставится метка "живой".

  3. Все объекты, не помеченные как живые, считаются "мусором".

  4. Они удаляются, а их память — освобождается.

📉 Пример

void example() {
User user = new User(); // создаётся объект в куче
user = null; // теперь на объект нет ссылок
}

После user = null объект типа User больше не достижим → GC может его удалить при следующей сборке.

🎯 Важные особенности

🔸 Сложные цепочки

GC умеет разбираться и в длинных и взаимно ссылающихся объектах:

A  B  C  A // если на A нет внешней ссылки  всё удалится

→ Даже если объекты ссылаются друг на друга, но вся цепочка недостижима от GC roots, они будут удалены.

🔸 Finalization

До удаления GC может вызвать finalize(), если он переопределён (но этот механизм устарел и не рекомендуется).

🧠 Что НЕ считается "мусором"

  • Объект, на который есть хотя бы одна активная ссылка (например, в стеке, в статике, в коллекции).

  • Объекты, на которые указаны из замыканий (lambda), слушателей, static-полей.

→ Такие объекты не будут удалены, даже если логически они "не нужны".

⚠️ Распространённые причины, по которым объект не считается мусором, хотя должен бы:

  • Забытая static ссылка (например, кеш).

  • Неудалённые слушатели (setOnClickListener, LiveData.observe()).

  • Замыкания (lambdas), захватывающие контекст (например, Activity).

  • Объекты, добавленные в коллекции, из которых их не удалили.

→ Такие ситуации вызывают утечки памяти, несмотря на наличие GC.

📌 Итог

Garbage Collector считает объект ненужным, если:

  • На него нет ссылок из GC roots напрямую или через другие объекты.

  • Он не достижим в графе объектов программы.

  • Он **не закреплён через статические поля, стэк вызова, активные потоки и т. д.
    **

После этого объект удаляется, а занимаемая им память — освобождается автоматически.