Understanding Garbage Collection (GC) and Stop-The-World (STW) in the JVM
Software Engineer - Backend
This guide explains — step by step — how the JVM’s garbage collector actually works.
We’ll start from how memory is structured (Eden, Survivor, Old), move to what happens during a Minor GC, then see what Stop-The-World (STW) really means, and finally understand how modern collectors like G1 use parallel and concurrent threads to balance speed and pause times.
1. The Purpose of Garbage Collection
The JVM automatically manages memory for Java programs.
When objects are no longer referenced, their memory must be reclaimed.
The role of the Garbage Collector (GC).
The GC’s challenge is to find dead objects without corrupting live ones — all while keeping the program fast and responsive.
2. How Memory Is Structured — Generations
The Java heap is divided into generations, based on object lifetime:
+--------------------------------------------+
| Java Heap |
+----------------------+---------------------+
| Young Gen | Old Gen |
+----------------------+---------------------+
2.1 Young Generation
The Young Generation holds newly created objects.
It has three parts:
+-----------------------------------+
| Young Generation |
+-----------+-----------+-----------+
| Eden | Survivor0 | Survivor1 |
+-----------+-----------+-----------+
Eden → where all new objects are allocated.
S0 and S1 (Survivor spaces) → temporary buffers for objects that survive collection.
Most objects die young, so GC frequently cleans Eden (fast and small).
3. How a Minor GC Works — Step-by-Step
Let’s see how objects move between Eden, Survivor spaces, and the Old Generation.
This process is called a Minor GC (affects only the Young Generation).
Step 1: Allocation
All new objects go into Eden.
Eden: [A, B, C, D, E]
S0: []
S1: []
Old: []
When Eden fills up → trigger a Minor GC.
Step 2: Minor GC #1 (Eden → S0)
JVM pauses all threads (Stop-The-World pause).
Finds which objects in Eden are still alive.
Copies those live objects into S0.
Discards the dead ones and clears Eden.
Eden: empty
S0: [A, B] (age = 1)
S1: []
Old: []
Now S0 is the “active survivor space.”
New allocations will again go into Eden.
Step 3: Minor GC #2 (Eden + S0 → S1)
When Eden fills up again:
JVM pauses threads (STW).
Marks live objects in Eden and S0.
Copies them into S1.
New objects from Eden → age = 1
Survivors from S0 → age = 2
Clears Eden and S0.
Eden: empty
S0: empty
S1: [A(age2), B(age2), X(age1)]
Old: []
Now S1 becomes the “from-space” (active survivor) for the next GC, and S0 becomes empty (“to-space”).
Step 4: Promotion to Old Generation
Each time an object survives a Minor GC, its age counter increases.
Once it crosses the threshold (default ≈15), it’s promoted to the Old Generation.
Old Gen holds long-lived objects (caches, singletons, large structures) and is collected less frequently.
4. Stop-The-World (STW) — Why Pauses Exist
During certain GC phases, all application threads are paused.
This is a Stop-The-World (STW) event.
It’s required when GC needs to safely scan thread stacks or registers — so no references change mid-scan.
Even modern collectors need short STW phases.
The difference is in how long those pauses last and how much work happens inside them.
5. Parallel vs Concurrent — The Two Kinds of Work
Here’s the distinction that most explanations blur:
| Term | Meaning |
| Parallel | Multiple GC threads working together while application threads are paused. |
| Concurrent | GC threads running alongside application threads (in background). |
G1 uses both.
6. G1 GC Phase Breakdown (Who Runs With Whom)
| Phase | App Threads | GC Threads | Description |
| Initial Mark | ❌ Paused | ✅ Parallel | Short STW pause to mark objects reachable from thread stacks. |
| Concurrent Mark | ✅ Running | ✅ Concurrent | GC threads trace reachable objects while app continues. |
| Remark | ❌ Paused | ✅ Parallel | Catches up references changed during concurrent mark. |
| Cleanup | ✅ Mostly running | ✅ Concurrent (some parallel) | Frees completely empty regions. |
| Evacuation (Copy / Compact) | ❌ Paused | ✅ Parallel | Copies live objects to new regions; main STW phase. |
What this means:
In parallel phases, application threads stop.
GC threads run in parallel with each other, not with the app.
Example: Initial Mark, Remark, Evacuation.In concurrent phases, GC threads run at the same time as the app.
Example: Concurrent Mark, Concurrent Cleanup.
G1 mixes both styles:
long phases are concurrent, short critical ones are parallel STW.
7. What Happens to the Old Generation (Major GC)
Over time, objects that survived many Minor GCs get promoted to the Old Generation.
When the Old Generation fills up, G1 runs a Mixed GC or Full GC.
Mixed GC (G1-specific)
G1 selects some Young regions and some Old regions with high garbage.
Evacuates live objects to new regions (parallel STW).
Reclaims garbage-heavy regions.
Full GC
Happens rarely (e.g., when G1 runs out of space).
Pauses all threads (global STW).
Marks, compacts, and rewrites the entire heap.
Fully parallel since JDK 10 (JEP 307).
8. Purpose of Evacuation in G1
The primary goal of evacuation is to eliminate fragmentation and keep allocation fast.
But there are also secondary benefits tied to GC efficiency and generational aging.
1️⃣ Prevent fragmentation
When G1 identifies regions full of garbage (Young or Old), it copies the live objects out into contiguous, clean regions (from the free list).
Then it reclaims the entire old region in one step.
Result:
No small “holes” left behind (no fragmentation).
Heap remains compact — all live objects are tightly packed.
Allocation for new objects stays fast (simple pointer bump).
2️⃣ Enable region reuse
After evacuation, the source regions become completely free and can be reassigned:
As new Eden regions for fresh allocations, or
As Survivor or Old regions for the next cycle.
This keeps heap layout flexible and adaptive.
3️⃣ Maintain object aging and promotion
During evacuation, G1 also decides:
Should a live object stay in Survivor (age < threshold)?
Or be promoted to Old (age > threshold)?
Enforces the generational policy while compacting memory.
✅ In short
Evacuation = copying live objects out of regions being reclaimed into clean free regions
→ prevents fragmentation, maintains fast allocation, enables region reuse, and handles object promotion.
It’s the operation that makes G1 both compact and generational without doing a full-heap compaction.
8. Summary — Putting it all together
| Concept | Description |
| Eden | Where new objects are allocated. Cleared after every Minor GC. |
| Survivor Spaces (S0/S1) | Objects that survive Minor GCs move here, alternating roles each cycle. |
| Old Generation | Holds long-lived objects. Collected less frequently. |
| Minor GC | Collects Young Gen only. Short STW pause. |
| Major/Mixed GC | Collects some or all Old Gen. Longer STW pause. |
| Parallel Phase | Many GC threads work together while app threads are stopped. |
| Concurrent Phase | GC threads run in background while app threads continue. |
9. Key Takeaway
Older GCs (Serial, Parallel) used one big global Stop-The-World phase.
Modern ones (like G1) split the work:
Do most marking and cleanup concurrently.
Do short bursts of copying and compaction in parallel during STW pauses.
That’s how G1 achieves predictable, low-latency GC even on large heaps.
The big shift isn’t what GCs do, but when and how much they can do without stopping the world.
Fact-checked sources:
References
