Skip to main content

Command Palette

Search for a command to run...

Understanding Garbage Collection (GC) and Stop-The-World (STW) in the JVM

Updated
7 min read
K

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)

  1. JVM pauses all threads (Stop-The-World pause).

  2. Finds which objects in Eden are still alive.

  3. Copies those live objects into S0.

  4. 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:

  1. JVM pauses threads (STW).

  2. Marks live objects in Eden and S0.

  3. Copies them into S1.

    • New objects from Eden → age = 1

    • Survivors from S0 → age = 2

  4. 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:

TermMeaning
ParallelMultiple GC threads working together while application threads are paused.
ConcurrentGC threads running alongside application threads (in background).

G1 uses both.


6. G1 GC Phase Breakdown (Who Runs With Whom)

PhaseApp ThreadsGC ThreadsDescription
Initial Mark❌ Paused✅ ParallelShort STW pause to mark objects reachable from thread stacks.
Concurrent Mark✅ Running✅ ConcurrentGC threads trace reachable objects while app continues.
Remark❌ Paused✅ ParallelCatches up references changed during concurrent mark.
Cleanup✅ Mostly running✅ Concurrent (some parallel)Frees completely empty regions.
Evacuation (Copy / Compact)❌ Paused✅ ParallelCopies 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

ConceptDescription
EdenWhere 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 GenerationHolds long-lived objects. Collected less frequently.
Minor GCCollects Young Gen only. Short STW pause.
Major/Mixed GCCollects some or all Old Gen. Longer STW pause.
Parallel PhaseMany GC threads work together while app threads are stopped.
Concurrent PhaseGC 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