Как сборщик мусора понимает, что объект можно уничтожить?
Сборщик мусора (Garbage Collector, GC) в Java определяет, что объект можно уничтожить, если на него невозможно больше сослаться из кода программы — то есть если он недостижим (unreachable) от так называемых корневых ссылок (GC Roots).
Важно: GC не смотрит на переменные "всё ещё не обнулённые" или "использованные недавно", а руководствуется графом достижимости объектов.
📌 Как GC понимает, что объект "мертвый"
🔗 1. Анализ достижимости от GC Roots
JVM строит направленный граф ссылок между объектами и определяет, какие объекты можно достигнуть от корневых узлов (GC Roots). Всё, что не достижимо, считается "мёртвым" и подлежит сборке.
💡 Что такое GC Roots?
GC Roots — это объекты, доступные всегда, даже если на них не ссылаются другие объекты:
-
Ссылки из стека текущих потоков (локальные переменные, параметры методов)
-
Ссылки из статических полей (static)
-
Ссылки из **JNI (native code)
** - Ссылки из **менеджеров классов
** - Ссылки из синхронизационных структур (мониторы, блокировки)
✅ Пример
public class Test {
public static void main(String\[\] args) {
Person p = new Person("Alice");
p = null;
}
}
Здесь:
-
Переменная p в стеке указывает на объект Person.
-
После p = null объект всё ещё в куче, но больше нет ссылок на него.
-
При следующем проходе GC обнаружит, что объект недостижим, и удалит его.
🔄 Граф достижимости (Reachability Graph)
GC строит дерево/граф:
GC Root
|
\[objA\] → \[objB\] → \[objC\]
Если objA достижим из GC Root, а objB и objC достижимы через него, они сохраняются. Если objA теряет связь с GC Root — вся цепочка может быть уничтожена.
🧠 Специальные уровни достижимости
Java 9+ (и раньше, в java.lang.ref):
-
Strong Reference — обычная ссылка (Person p = new Person()): объект жив до обнуления этой переменной.
-
Soft Reference — объект удаляется только при нехватке памяти.
-
Weak Reference — объект удаляется при первом проходе GC, если нет других strong-ссылок.
-
Phantom Reference — используется для отслеживания уничтожения объектов (без доступа к ним).
🔥 Циклические ссылки — НЕ проблема
GC умеет обнаруживать циклы. Даже если два объекта ссылаются друг на друга, но при этом они не достижимы от GC Roots, они будут удалены.
class A {
B b;
}
class B {
A a;
}
Если ни A, ни B не доступны из GC Roots — цикл будет удалён.
🔍 Как GC это делает?
Зависит от типа сборщика мусора:
-
Mark-and-Sweep:
-
Mark — проходит от GC Roots и помечает все достижимые объекты.
-
Sweep — всё непомеченное удаляется.
-
-
Generational GC (у большинства JVM):
-
Молодые объекты (в Eden) собираются чаще.
-
Старые объекты (Old Gen) — реже.
-
-
G1, ZGC, Shenandoah — делают это параллельно и инкрементально, избегая долгих пауз.
📦 Когда объект действительно уничтожается?
-
GC определяет, что объект не достижим.
-
Вызывается (опционально) его метод finalize() (до Java 9; позже deprecated).
-
Объект удаляется физически и память возвращается в кучу.
📌 Важно помнить
-
GC никогда не гарантирует немедленного удаления объекта после потери ссылки.
-
Вызывать System.gc() — только в исключительных случаях, это необязательная рекомендация для GC.
-
Если объекты "живут слишком долго", возможна утечка памяти — когда ссылки существуют, но объекты уже не нужны (например, кэш, статическое поле, слушатель событий).
Таким образом, сборщик мусора считает объект пригодным к удалению тогда, когда на него нельзя попасть, начиная от GC Roots. Именно достижимость, а не "использовался ли объект недавно", определяет судьбу объекта в куче.