Garbage Collection: Lifecycle, Algorithms & STW
HotSpot reclaims unreachable heap objects automatically. This page is the interactive home for how an object ages (Eden β Survivors β Old β reclaimed), which algorithms young/old use, and how collectors evolved to shrink Stop-The-World (STW) pauses.
For JVM architecture (Heap, Stack, PC, Metaspace) see JVM Internals.
1. Why Generational GC?
Weak generational hypothesis:
- Most objects die young (request DTOs, short-lived builders, temporaries).
- Objects that survive early collections tend to live a long time (singletons, caches, pools).
HotSpot splits the Java Heap into a Young generation (cheap, frequent Mark-Copy) and an Old generation (rarer, more expensive cycles). That split is why S0/S1 exist and why Minor GC is usually cheap compared to Full GC.
2. Heap Layout: Eden, S0, S1, Old
Click regions for ratios, GC role, and flags.
Heap Memory (GC-Managed Shared Area)
Capacity Ratio: 100% of Heap Allocation
Overview: The primary memory workspace of the JVM where all objects, class instances, and array payloads are allocated.
- GC Role & Management: Scanned continuously by minor, major, and mixed GC threads to recover unreachable blocks.
- JVM Sizing Flags:
- -Xms (Initial Heap size)
- -Xmx (Maximum Heap size)
- -XX:MinHeapFreeRatio
- -XX:MaxHeapFreeRatio
π‘ Click on any segment (Heap, Young Gen, Eden, S0, S1, or Old Gen) in the diagram above to inspect JVM memory sizing parameters.
| Space | Role |
|---|---|
| Eden | Birthplace of almost all new allocations (TLAB) |
| S0 / S1 | Twin survivor spaces; one is empty (To) before each young GC; roles flip |
| Old (Tenured) | Long-lived objects after age threshold or survivor overflow |
Locals vs objects: method locals and parameters live on the stack and vanish when the frame returns. Only heap objects go through Eden β Survivor β Old.
3. Object Lifecycle (Animate)
Follow one object from allocation to reclamation. Press βΆ Animate or click a stage.
S0 and S1 swap From/To each Minor GC. Age lives in the object header until promotion or death in Young.
Lifecycle in one breath
- Allocate in Eden.
- Minor GC copies live Eden objects into a Survivor (To); Eden is wiped.
- Next Minor GC: live objects in From + Eden copy into the other Survivor; age++; spaces swap.
- Age β₯
-XX:MaxTenuringThreshold(default 15) or Survivor pressure β promote to Old. - When unreachable from GC Roots, a later Old / mixed / concurrent cycle reclaims the memory.
4. How GC Decides βGarbageβ
HotSpot does not use reference counting (circular refs would leak). It uses reachability analysis from GC Roots:
- Locals / parameters on active thread stacks
- Static fields
- JNI references
- Threads / some internal JVM handles
Anything not reachable from a root is garbage β even if objects still point at each other.
A Java memory leak is usually βstill reachable from a root but forgotten by the appβ (static Map, ThreadLocal, listeners) β GC cannot help until the reference is cleared.
5. Algorithms & STW Evolution
Select a collector on the timeline, then an algorithm chip. Highlighted chips are the ones that collector relies on.
Default on modern HotSpot. Heap is split into equal regions (Eden/Survivor/Old/Humongous). G1 collects regions with the most garbage first under a pause-time goal, using concurrent marking and young/mixed evacuations.
- Remembered sets track cross-region refs so a region can be collected without scanning the world.
- SATB write barrier during concurrent mark.
- Mixed GC: young + selected old regions in one pause window.
- Still has STW β engineered to meet -XX:MaxGCPauseMillis, not zero.
- to-space exhausted / evacuation failure β Full GC (look for Pause Full in logs).
- Humongous allocation storms fragment region map.
- Tune pause goal + heap size together; tiny heap + tiny pause is impossible.
- Default choice unless latency SLO demands ZGC.
-XX:+UseG1GC-XX:MaxGCPauseMillis=200-XX:InitiatingHeapOccupancyPercentSimulate Mark-Copy (young Minor GC) or Mark-Compact (old slide) with moving arrows β one focused sim, not every collector.
Eden is full of live (green) and dead (gray) objects. From may hold survivors from the last cycle. To is empty β required spare space for Mark-Copy.
- Young layout: Eden + From + To (S0/S1 with flipped roles).
- Dead objects are never visited one-by-one later β only live ones are copied.
- STW begins: mutator threads pause for the young collection.
- Cost will scale with live bytes in Eden+From, not with how many dead objects exist.
Mark-Copy: evacuate live objects into empty To, abandon dead space, flip survivors.
Long STW freezes TCP handling β Kubernetes probes can kill a pod that is only βpaused,β not dead. That operational pain is why G1 pause goals and ZGC concurrent relocate exist.
6. G1 Regions
G1 divides the heap into equal-sized regions. Each region is Eden, Survivor, Old, or Humongous. Click for region semantics.
Eden Region (Young Generation)
Overview: Logical young generation region. Sized dynamically based on allocation throughput.
- Tuning flags: -XX:G1NewSizePercent (Initial size, default 5%) | -XX:G1MaxNewSizePercent (Max size, default 60%)
- GC Collection Behavior: Minor GCs evacuate surviving objects to Survivor regions, emptying the Eden regions completely to become Free.
- Under the Hood Mechanics:
- Objects are allocated here via TLABs to avoid synchronization locks.
- Sizing is adjusted dynamically by G1 between collections to meet user pause time targets (-XX:MaxGCPauseMillis).
π‘ Click on any region box (Eden, Survivor, Old, Humongous, Free) on the grid to inspect its logical partition rules.
- Young GC: evacuate Eden + Survivors into survivor/old regions.
- Concurrent mark: find reclaimable Old regions.
- Mixed GC: young + high-garbage Old regions in one pause budget (
MaxGCPauseMillis). - Full GC in logs (
Pause Full) means evacuation/concurrent cycle failed to keep up β investigate promotion, humongous objects, or heap size.
7. Tri-Color Marking & Concurrent Collector Barriers
Modern low-latency collectors (G1, Shenandoah, ZGC) perform object marking concurrently while application worker threads (mutators) continue to run. They model object reachability using the Tri-Color Marking Abstraction:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRI-COLOR MARKING ABSTRACTION β
β β
β β’ WHITE: Unvisited objects. At the end of marking, white objects are dead. β
β β’ GREY: Visited by GC, but its outgoing field references are not yet scannedβ
β β’ BLACK: Visited AND all outgoing references scanned. Guaranteed live. β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The Concurrent Invalidation Hazard
Because mutator threads run simultaneously with the GC marking threads, a mutator can break reachability assumptions if:
- Mutator clears a pointer from a Grey object to a White object.
- Mutator attaches that White object to an already-scanned Black object.
- If unhandled, the GC never visits the white object (since black objects are never rescanned), resulting in catastrophic silent data corruption (premature reclamation of live objects!).
Barriers: SATB vs. Incremental Update vs. Load Barrier
- Snapshot-At-The-Beginning (SATB - G1 GC): Uses a Pre-Write Barrier. Before overwriting an old field reference, it logs the old reference into a per-thread SATB buffer. Guarantees that any object that was live at the start of GC remains considered live.
- Incremental Update (CMS / Shenandoah): Uses a Post-Write Barrier. When a black object receives a reference to a white object, it downgrades the black object back to grey.
- Load Barrier (ZGC): Intercepts reference reads (
getfield) rather than writes. If an object pointer has not been remapped, the load barrier intercepts the read in ~2 nanoseconds, updates the pointer to its new relocated address, and returns the valid object immediately ("Self-Healing").
8. ZGC Colored Pointers & Generational ZGC (Java 21)
ZGC achieves sub-millisecond pause times on heaps ranging from 8MB to 16TB by executing all mark, evacuate, and relocate phases concurrently.
Colored Pointers & Virtual Memory Multi-Mapping
On 64-bit platforms, ZGC stores metadata directly inside the unused high-order bits of the 64-bit object reference pointer:
+-------------------+-------------+-----------------------------------------------+
| 16 Unused Bits | 4 Color Bits| 44-bit Object Offset (Addresses up to 16 TB) |
+-------------------+-------------+-----------------------------------------------+
| | | |
| | | +-- Finalizable (Weak/Phantom reference tracking)
| | +---- Remapped (Address points to new relocated location)
| +------ Marked1 (Active live marking phase)
+-------- Marked0 (Alternate live marking phase)
- OS Virtual Memory Multi-Mapping (
mmap): The Linux kernel maps the same physical memory page to three separate virtual memory addresses (Marked0,Marked1, andRemapped). When the CPU dereferences a colored pointer, it hits the exact same physical heap page regardless of which color bit is active, avoiding software bitmasking penalties.
Generational ZGC (JEP 439 - Java 21 LTS)
In Java 21, Generational ZGC (-XX:+UseZGC -XX:+ZGenerational) separated the ZGC heap into Young and Old generations:
- Young collections run frequently and collect 90%+ of short-lived objects with almost zero CPU overhead.
- Resolves the legacy ZGC "Allocation Stall" issue where high allocation rates outpaced the single-generation concurrent marking cycle.
- Delivers consistent p99.99 latencies under 1 millisecond while maintaining throughput comparable to G1 GC.
9. Flags Cheatsheet
| Flag | Meaning |
|---|---|
-Xms / -Xmx | Heap size (keep headroom for non-heap RSS in containers) |
-Xmn / -XX:NewRatio | Young size |
-XX:SurvivorRatio | Eden vs each Survivor (default 8 β ~8:1:1) |
-XX:MaxTenuringThreshold | Max age before promotion (default 15) |
-XX:+UseG1GC | G1 (default JDK 9+) |
-XX:MaxGCPauseMillis | G1 pause goal (soft, default 200ms) |
-XX:InitiatingHeapOccupancyPercent | When G1 starts concurrent mark (default 45%) |
-XX:+UseZGC / -XX:+ZGenerational | Generational ZGC (sub-millisecond pauses on JDK 21+) |
-Xlog:gc*,gc+phases=debug:file=gc.log:time,uptime,pid:filecount=5,filesize=100M | Production Unified GC logging |
10. Interview Hooks
- Explain S0/S1 From/To flip and why one survivor is empty after a young GC.
- Parallel vs concurrent: Parallel = many GC threads but STW; concurrent = mutators run during mark/relocate.
- Why Mark-Copy for Young and why whole-heap copy is a bad Old strategy.
- Tri-color marking: How write barriers (SATB) prevent premature reclamation of live objects.
- ZGC Colored Pointers & Load Barriers: How self-healing pointers achieve sub-millisecond pauses without Stop-The-World relocation phases.
- When to stay on G1 (maximum batch throughput) vs move to Generational ZGC (strict <1ms SLA requirements).
