skip to content

Reading a HotSpot garbage-collection log, how do you tell a young (minor) collection from a mixed collection and from a full collection, and why is spotting a full collection important?

level: middleimportance: must knowfreq 58%

answer

  1. Scope word after Pause: Young / Young (Mixed) / Full
  2. Mixed = young + a bounded batch of old regions (G1)
  3. Full on G1/ZGC/Shenandoah = fallback, count it
  4. To-space exhausted → evacuation failure → Full
  5. Concurrent lines are not pauses

basics

~20 s

Young collections log Pause Young, G1's mixed ones Pause Young (Mixed) (young plus some old regions), and full ones Pause Full, which collects and compacts the whole heap in one long stop. Under G1 or a concurrent collector, a Full GC signals the concurrent machinery failed to keep up.

solid answer

~60 s

The scope word after `Pause` tells you which is which. - **`Pause Young (Normal)`** — eden and survivors only. Frequent, short, proportional to the *surviving* data, not to heap size. Thousands of these are normal. - **`Pause Young (Mixed)`** — G1 only: the same evacuation, plus a set of old-generation regions chosen by the preceding concurrent mark. This is how G1 normally reclaims the old generation, incrementally, still bounded by the pause target. - **`Pause Full`** — the entire heap is traced and compacted in one stop-the-world event. Duration scales with live data; on a multi-gigabyte heap it is hundreds of milliseconds to seconds. - **`Concurrent Mark Cycle`** and friends are *not* pauses at all. For Serial and Parallel, full collections are the ordinary way the old generation is reclaimed. For G1, ZGC and Shenandoah they are a **fallback**: allocation outran the concurrent cycle (`To-space exhausted`, `Allocation Stall`), or something called `System.gc()`. So on those collectors a Full GC in the log is a finding, not a formality — you count them, chase the cause, and fix the cause rather than the symptom.

go deeper

for a junior

Distinguish Pause Young from Pause Full and know that full collections stop everything and cost far more.

for a middle

Add mixed collections and G1's cycle vocabulary, and read the cause string to explain why each collection was triggered.

for a senior

Treat Full GC frequency as an SLO signal, trace To-space exhausted, humongous allocation and System.gc() back to root causes, and judge them from the pattern across a window rather than one event.

for a principal

Set the fleet policy: alert on any Full GC under a concurrent collector, and decide when repeated fallbacks mean re-sizing versus a change of collector or of the allocation behaviour itself.

## The three shapes, and where they come from All HotSpot generational collectors split work by *scope*, and the log names the scope explicitly. **Young / minor collection.** Only eden and the survivor spaces are collected. Live objects are copied to a survivor space or promoted to the old generation; everything else is abandoned wholesale. Cost is proportional to what *survives*, which in a well-behaved application is a small fraction of what was allocated — that is why young collections are cheap and can be frequent. Log lines read `Pause Young (Normal) (G1 Evacuation Pause)` under G1 or `Pause Young (Allocation Failure)` under Serial/Parallel. **Mixed collection (G1).** After a concurrent mark cycle has identified which old regions hold the most garbage, G1 schedules a series of collections that evacuate the young generation *plus* a bounded batch of those old regions: `Pause Young (Mixed)`. The batch size is chosen to stay inside the pause target, which is precisely how G1 reclaims the old generation without one giant stop. Seeing mixed collections is healthy; it means the concurrent cycle completed and G1 is keeping the old generation trimmed. The related `Pause Young (Concurrent Start)` marks the young collection that also kicks off a concurrent mark cycle, and `Pause Young (Prepare Mixed)` is the last young-only one before the mixed series begins. **Full collection.** The whole heap — young and old, plus class metadata handling — is traced, and in G1's case compacted by a stop-the-world algorithm that is deliberately not the optimised concurrent path. `Pause Full (G1 Compaction Pause)`. Cost scales with the *live set*, so a heap with 8 GB of live data pays seconds. Every application thread waits. ## Why a Full GC means different things on different collectors On **Serial** and **Parallel**, old-generation reclamation *is* the full collection. A steady rhythm of full collections, each reclaiming a lot and completing in a time your latency budget tolerates, is the design working as intended. On **G1**, **ZGC** and **Shenandoah**, the old generation is meant to be reclaimed concurrently or in bounded mixed pauses. A `Pause Full` therefore means a fallback fired. The usual triggers: - **Evacuation failure / to-space exhausted** — G1 had no free region to copy survivors into. The log shows `To-space exhausted` just before, and often a burst of humongous allocations before that. The concurrent cycle started too late or ran too slowly relative to the allocation rate. - **Allocation stall** (ZGC/Shenandoah vocabulary) — threads were made to wait for memory because the concurrent collector was still working. Same root cause: allocation outran collection. - **`System.gc()`** — an explicit call, from application code, a library, or an RMI distributed-GC timer. The cause string names it; `-XX:+DisableExplicitGC` suppresses it, but finding the caller is better. - **Metadata GC Threshold** — class metadata, not the heap, ran short; the fix lives in metaspace sizing or in whatever is generating classes. - **Heap Dump / Diagnostic Command Initiated GC** — a tool asked for one. ## Reading the pattern, not the event What you extract from a log is a *rhythm*: - Young pauses: how often, how long, how much survives each time. Rising survival at constant allocation means data is living longer. - Mixed cycles: are they happening at all, and do they bring old occupancy down? - Full collections: count them per hour. Zero is the target on a concurrent collector. - The used-after value across collections: flat is healthy; a staircase that never returns to its floor and culminates in back-to-back full collections reclaiming almost nothing is the classic signature of a heap that is too small for the live set, or of unbounded retention. Back-to-back full collections with a tiny gap between used-before and used-after mean the JVM is spending nearly all its time collecting and reclaiming nearly nothing — the state that eventually produces `OutOfMemoryError: GC overhead limit exceeded` on collectors that implement that guard. ## Distinguishing pauses from concurrent work One persistent misreading: lines beginning `Concurrent` (`Concurrent Mark Cycle`, `Concurrent Undo Cycle`, concurrent phase lines under ZGC/Shenandoah) run while application threads execute. They consume CPU and can lengthen request latency indirectly, but they are not stop-the-world time. Only `Pause` lines belong in a pause budget. Summing everything produces a frightening and wrong number.

  • A G1 log shows `To-space exhausted` immediately before a Full GC. What happened, and what would you change?
    G1 ran out of free regions to copy survivors into during evacuation, so the collection failed and fell back to a stop-the-world compaction. It means allocation and promotion outran the concurrent cycle. Typical remedies are giving the heap more headroom, starting the concurrent cycle earlier via `-XX:InitiatingHeapOccupancyPercent`, allowing more concurrent GC threads, or — best — reducing the allocation or retention that caused it.
  • How do you find out what is calling `System.gc()` when the log names it as the cause?
    Take the timestamps of the `System.gc()` collections and correlate them with application activity; a regular hourly cadence points at RMI's distributed GC, while irregular ones usually come from a library or a direct-ByteBuffer cleanup path. A JFR recording or a breakpoint/agent on `System.gc` pins the exact caller. `-XX:+DisableExplicitGC` stops the symptom but the caller is still worth identifying.

saying these in an interview costs you the question

  • Calling every collection in the log a "GC" without distinguishing young, mixed and full, and so missing the one that matters.
  • Treating G1 Full GCs as routine rather than as evidence that the concurrent cycle lost the race.
  • Believing mixed collections are a failure mode; they are G1's normal way to reclaim the old generation.
  • Counting `Concurrent Mark Cycle` duration as stop-the-world pause time.
  • Reacting to full collections by enlarging the heap without first reading the cause on the line.

context