What are GC roots?
4. Handshakes (Java 11+) → fewer global STW 5. Minimize thread count
Junior Level
GC Roots — “root” objects from which GC starts searching for live objects.
Simple analogy: A tree. Roots are GC Roots. If a branch (object) can be reached from a root → it’s alive.
What are GC Roots:
- Local variables in methods
- Static fields of classes
- Active threads
- JNI references
Rule: Object can be reached from GC Root → object is alive.
Middle Level
Full Classification
GC Roots:
├── Stack Locals (local variables)
├── Static Fields (static fields)
├── Thread Objects (active threads)
├── JNI Global (global native references)
├── JNI Local (local native references)
├── Monitor Used (objects in synchronized)
└── JVM Internal (system objects)
OopMaps
OopMaps (Ordinary Object Pointer Maps): JIT creates a map where each byte of
code specifies which local variables contain references. Without OopMaps, GC
would have to scan every byte of the stack — slow, plus false positives
(normal int values that accidentally look like pointers).
→ At Safepoints, JIT knows which registers/offsets = references
→ Root Scanning in milliseconds
Path to GC Roots (MAT)
MAT: Path to GC Roots
→ exclude soft/weak/phantom → strong references only
→ Shows who's holding the object
→ 99% of leaks = strong references from Static or Thread
Senior Level
Root Scanning and Latency
Number of Roots affects pauses:
→ 1000 threads → long stack scanning
→ Millions of keys in static HashMap
Modern Java (Handshakes):
→ Per-thread scanning
→ Minimized global pause
JNI Global Reference Leaks
Native code created global reference:
→ NewGlobalRef(obj)
→ Forgot DeleteGlobalRef
→ Object won't be GC'd!
→ "Invisible" leak
MAT shows JNI Global References as GC Roots. They appear as
"JNI Global" in Path to GC Roots view. But JNI Local References (references
in native code) are not visible to MAT — this is a "blind spot".
Finalizer Queue
Objects with finalize():
→ After death, go to Finalizer Queue
→ Become temporary GC Root
→ Until finalize() executes → object alive
→ Delayed removal
→ Reference Handler thread processes queue
Best Practices
- Monitor Root Scanning time in GC logs
- JNI → DeleteGlobalRef mandatory
- exclude weak/soft in MAT
- Handshakes (Java 11+) → fewer global STW
- Minimize thread count
Senior Summary
- GC Roots = foundation of Reachability Analysis
- OopMaps = fast scanning
- JNI Global = invisible leaks
- Root Scanning time → indicator of problems
- Path to GC Roots = main MAT tool
- Finalizer Queue = temporary GC Root
Interview Cheat Sheet
Must know:
- GC Roots — root objects from which GC starts reachability traversal: Stack Locals, Static Fields, Thread Objects, JNI Global, JNI Local, Monitor Used, JVM Internal
- OopMaps: JIT creates a map where each byte of code specifies which variables contain references; without OopMaps, GC would scan every byte (slow + false positives)
- Path to GC Roots (MAT): exclude soft/weak/phantom → strong references only; 99% of leaks = references from Static or Thread
- JNI Global Reference leaks:
NewGlobalRef(obj)withoutDeleteGlobalRef→ object won’t be GC’d; MAT shows as “JNI Global” in Path to GC Roots - Finalizer Queue: objects with
finalize()become temporary GC Root after death; untilfinalize()executes → object alive - Root Scanning time: number of Roots affects pauses; 1000 threads → long stack scanning; Handshakes (Java 11+) minimize global pause
Common follow-up questions:
- Why are OopMaps important for performance? — Without OopMaps, GC scans every byte of stack → slow + false positives (int that accidentally looks like pointer)
- How does JNI Global Reference cause a leak? — Native code created global reference and forgot to delete; GC sees it as GC Root → object never deleted
- Why is Finalizer Queue a temporary GC Root? — Object with
finalize()goes to Finalizer Queue after death; untilfinalize()executes → object alive → removal delay - What is Root Scanning time in GC logs? — Time to scan all GC Roots; if growing → too many threads or static references
Red flags (DO NOT say):
- “GC Roots are only static fields” — also includes Stack Locals, Thread Objects, JNI references, Monitor objects
- “JNI leaks are visible in Heap Dump” — JNI Global visible, but JNI Local (in native code) is MAT “blind spot”
- “finalize() is fast and safe” — object alive until
finalize()executes; Reference Handler thread processes queue asynchronously
Related topics:
- [[5. When does an object become eligible for GC]]
- [[26. What is reachability in the context of GC]]
- [[4. What is Garbage Collection]]
- [[21. What is a memory leak and how to detect it]]
- [[27. Can you manually invoke GC]]