Что такое GC roots?
4. Handshakes (Java 11+) → меньше STW 5. Минимизируйте количество потоков
🟢 Junior Level
GC Roots — это “корневые” объекты, от которых GC начинает поиск живых объектов.
Простая аналогия: Дерево. Корни — это GC Roots. Если до ветки (объекта) можно добраться от корня → она жива.
Что является GC Roots:
- Локальные переменные в методах
- Статические поля классов
- Активные потоки
- JNI ссылки
Правило: До объекта можно добраться от GC Root → объект жив.
🟡 Middle Level
Полная классификация
GC Roots:
├── Stack Locals (локальные переменные)
├── Static Fields (статические поля)
├── Thread Objects (активные потоки)
├── JNI Global (глобальные нативные ссылки)
├── JNI Local (локальные нативные ссылки)
├── Monitor Used (объекты в synchronized)
└── JVM Internal (системные объекты)
OopMaps
OopMaps (Ordinary Object Pointer Maps): JIT создаёт карту, где в каждом байте
кода указано, какие локальные переменные содержат ссылки. Без OopMaps GC
пришлось бы сканировать каждый байт стека — медленно, плюс ложные срабатывания
(обычные int-значения, случайно похожие на указатели).
→ В Safepoints JIT знает, какие регистры/смещения = ссылки
→ Root Scanning за миллисекунды
Path to GC Roots (MAT)
MAT: Path to GC Roots
→ exclude soft/weak/phantom → только сильные ссылки
→ Показывает, кто держит объект
→ 99% утечек = сильные ссылки от Static или Thread
🔴 Senior Level
Root Scanning и Latency
Количество Roots влияет на паузы:
→ 1000 потоков → долгое сканирование стеков
→ Миллионы ключей в static HashMap
Modern Java (Handshakes):
→ Сканирование потоков по отдельности
→ Минимизация глобальной паузы
JNI Global Reference утечки
Нативный код создал глобальную ссылку:
→ NewGlobalRef(obj)
→ Забыл DeleteGlobalRef
→ Объект не удалится GC!
→ "Невидимая" утечка
MAT показывает JNI Global References как GC Roots. Они отображаются как
"JNI Global" в Path to GC Roots view. Но JNI Local References (ссылки
в native-коде) MAT не видит — это «слепая зона».
Finalizer Queue
Объекты с finalize():
→ После смерти попадают в Finalizer Queue
→ Становятся временным GC Root
→ Пока finalize() не выполнится → объект жив
→ Задержка удаления
→ Reference Handler поток обрабатывает очередь
Best Practices
- Мониторьте Root Scanning time в GC логах
- JNI → DeleteGlobalRef обязательно
- exclude weak/soft в MAT
- Handshakes (Java 11+) → меньше STW
- Минимизируйте количество потоков
Резюме для Senior
- GC Roots = фундамент Reachability Analysis
- OopMaps = быстрое сканирование
- JNI Global = невидимые утечки
- Root Scanning time → индикатор проблем
- Path to GC Roots = главный инструмент MAT
- Finalizer Queue = временный GC Root
🎯 Шпаргалка для интервью
Обязательно знать:
- GC Roots — корневые объекты, от которых GC начинает обход достижимости: Stack Locals, Static Fields, Thread Objects, JNI Global, JNI Local, Monitor Used, JVM Internal
- OopMaps: JIT создаёт карту, где в каждом байте кода указано, какие переменные содержат ссылки; без OopMaps GC сканировал бы каждый байт (медленно + ложные срабатывания)
- Path to GC Roots (MAT): exclude soft/weak/phantom → только сильные ссылки; 99% утечек = ссылки от Static или Thread
- JNI Global Reference утечки:
NewGlobalRef(obj)безDeleteGlobalRef→ объект не удалится GC; MAT показывает как «JNI Global» в Path to GC Roots - Finalizer Queue: объекты с
finalize()после смерти становятся временным GC Root; покаfinalize()не выполнится → объект жив - Root Scanning time: количество Roots влияет на паузы; 1000 потоков → долгое сканирование стеков; Handshakes (Java 11+) минимизируют глобальную паузу
Частые уточняющие вопросы:
- Почему OopMaps важны для производительности? — Без OopMaps GC сканирует каждый байт стека → медленно + ложные срабатывания (int, случайно похожий на указатель)
- Как JNI Global Reference вызывает утечку? — Нативный код создал глобальную ссылку и забыл удалить; GC видит её как GC Root → объект никогда не удалится
- Почему Finalizer Queue — временный GC Root? — Объект с
finalize()после смерти попадает в Finalizer Queue; покаfinalize()не выполнится → объект жив → задержка удаления - Что такое Root Scanning time в GC логах? — Время сканирования всех GC Roots; если растёт → слишком много потоков или статических ссылок
Красные флаги (НЕ говорить):
- «GC Roots — это только static поля» —还包括 Stack Locals, Thread Objects, JNI ссылки, Monitor объекты
- «JNI утечки видны в Heap Dump» — JNI Global видны, но JNI Local (в native-коде) — «слепая зона» MAT
- «finalize() быстрый и безопасный» — объект жив, пока
finalize()не выполнится; Reference Handler поток обрабатывает очередь асинхронно
Связанные темы:
- [[5. Когда объект становится кандидатом на удаление GC]]
- [[26. Что такое reachability в контексте GC]]
- [[4. Что такое Garbage Collection]]
- [[21. Что такое memory leak и как его обнаружить]]
- [[27. Можно ли вручную вызвать GC]]