Как garbage collector понимает, что объект не нужен?
Garbage Collector (GC) в Java определяет, что объект не нужен (т.е. может быть удалён), на основе его достижимости. Если объект не достижим от активных ссылок в программе, он считается "мусором" и подлежит удалению.
📌 Ключевой принцип — достижимость (reachability)
Объект считается живым, если:
-
На него можно добраться из кода, напрямую или через цепочку других объектов.
-
Он доступен от корневых ссылок (GC roots).
Если объект не достижим от GC roots, он считается мертвым — и GC освободит память, удалив его из кучи.
🔗 Что такое GC roots?
Это начальные точки, от которых начинается обход объектов. К ним относятся:
-
Локальные переменные текущих методов (в стеке)
-
Аргументы методов
-
Ссылки из статических полей (static)
-
Ссылки из активных потоков (Thread)
-
Объекты JNI (native-код)
🔄 Как работает определение "ненужного" объекта?
-
GC запускает обход от GC roots.
-
Каждому достижимому объекту ставится метка "живой".
-
Все объекты, не помеченные как живые, считаются "мусором".
-
Они удаляются, а их память — освобождается.
📉 Пример
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 напрямую или через другие объекты.
-
Он не достижим в графе объектов программы.
-
Он **не закреплён через статические поля, стэк вызова, активные потоки и т. д.
**
После этого объект удаляется, а занимаемая им память — освобождается автоматически.