Why architecture matters here

The reason G1's architecture matters is that garbage collection pauses are the dominant source of tail latency in most JVM services. A request that would normally take 20 milliseconds can take 800 if it happens to arrive during a stop-the-world collection, and it is precisely those rare slow requests — the 99.9th percentile — that violate SLAs, trip circuit breakers, and cascade into retries and overload. A collector that keeps average throughput high but occasionally stops the world for a full second is worse, for a latency-sensitive service, than one with slightly lower throughput and no pause ever exceeding a predictable bound. G1's whole design is an answer to that tradeoff: trade a few percent of throughput for the ability to say 'no pause will exceed roughly 200 milliseconds.'

The key architectural idea is incrementality with prediction. Because the heap is regions, G1 can choose how much to collect each pause, and because it records how long past collections took relative to how many regions and live bytes they involved, it can predict how many regions it can afford to include this time while staying under the pause target. Every mixed collection is therefore a small optimization problem: given a pause budget, pick the set of old regions whose garbage yield is highest per unit of copying cost. This is why G1 tends to degrade gracefully under memory pressure rather than falling off a cliff — it collects a little less each pause instead of one giant pause.

The second reason architecture matters is that G1's guarantees are conditional, and the conditions are things an operator controls. The pause target is honored only if the collector can keep up — if the application allocates faster than G1 can reclaim, or if the heap is too small for the live-set plus allocation rate, G1 is forced into a full GC: a single-threaded (historically) or parallel stop-the-world collection of the entire heap, which is exactly the pause G1 exists to avoid. Understanding what pushes G1 into that failure mode — humongous allocation churn, insufficient heap, a marking cycle that cannot finish in time — is the difference between a service that hums along at its pause target and one that periodically freezes for two seconds.

Finally, the region model changes how you reason about memory. Concepts that were simple in a two-space collector — 'the old generation is 4GB' — become distributions over regions, and pathologies like humongous objects (anything larger than half a region) get their own allocation path and their own failure modes. Reasoning about G1 means reasoning about regions, remembered-set overhead, and the marking cycle's progress relative to allocation, not about monolithic generations.

Advertisement

The architecture: every piece explained

The top row is the heap layout. G1 divides the heap into equal-sized regions whose size is a power of two chosen at startup (1MB to 32MB) so that a heap has roughly 2,048 regions. Each region is tagged with a role: eden and survivor regions form the young generation; old regions hold tenured objects; and humongous regions hold single objects larger than half a region, which get their own contiguous-region allocation because they cannot fit the normal eden path. The roles are fluid — an eden region that survives becomes survivor, a survivor that ages enough becomes old — so the generational split is logical, laid over a physical region array.

The middle row is the collection machinery. A young collection is a stop-the-world pause that evacuates all live objects out of eden (and aging survivors) into fresh survivor or old regions, then frees the now-empty eden regions wholesale — this is a copying collection, so it compacts as it goes and never fragments. When the old generation's occupancy crosses a threshold (the initiating heap occupancy percent, IHOP), G1 starts a concurrent marking cycle: mostly concurrent with the application, it traces the live object graph to compute per-region liveness, using the snapshot-at-the-beginning (SATB) invariant so that objects live at the cycle's start are treated as live even if they die during marking. Once marking knows which old regions are mostly garbage, G1 runs mixed collections: young collections that additionally evacuate a chosen batch of the garbage-heaviest old regions.

The right of that row is the pause predictor. G1 keeps a model of collection cost — how long it took to evacuate N regions with M live bytes and K remembered-set entries — and uses it to assemble each collection's collection set: the regions to collect this pause, sized so the predicted pause stays under the MaxGCPauseMillis target. For mixed collections it greedily adds the highest-garbage old regions until the budget is spent, which is the Garbage-First heuristic in action.

The bottom rows are the correctness infrastructure. Remembered sets (RSets) record, per region, which other regions hold references pointing into it; maintained by a write barrier that runs on every reference store, they let G1 collect a region without scanning the whole heap for inbound pointers — it scans only the RSet. Evacuation and compaction is the copying itself: live objects are copied out of collected regions into free ones, references are updated, and the source regions are returned to the free list, empty and defragmented. The ops strip — heap sizing, pause target, humongous tuning, GC log analysis — is where an operator's choices meet the collector's mechanics.

G1 GC — region-based, incremental, pause-time-targeted collectorcollect the garbage-first regions, meet a pause goalHeap = regions1-32MB equal regionsEden / Survivoryoung generationOld regionstenured objectsHumongousobjects > 50% regionYoung collectionSTW, evacuate edenConcurrent markSATB livenessMixed collectionyoung + best oldPause predictorpicks collection setRemembered sets + write barriertrack cross-region refsEvacuation + compactioncopy live, free regionOps — heap sizing + pause target + humongous tuning + GC log analysisfillspromotemarkpredictbarrierSATBcopyoperateoperate
G1 GC: a region-partitioned heap collected incrementally — young evacuations, concurrent SATB marking, and mixed collections whose collection set is chosen by a pause-time predictor.
Advertisement

End-to-end flow

Follow a service from a fresh heap. Threads allocate objects, which land in eden regions. The write barrier fires on reference stores, quietly maintaining remembered sets and the SATB marking queues. Eden fills; when the next allocation cannot find a free eden region, G1 triggers a young collection. All application threads stop. G1 scans the roots (thread stacks, registers, static fields) plus the remembered sets of the collected regions — this is why RSets matter: they let G1 find inbound references without scanning old regions — and copies every live eden object into a survivor region (or straight to old if it has aged past the tenuring threshold). The empty eden regions are freed. The pause is short because only young regions were touched, and typical young pauses are a few milliseconds.

Over many young collections, objects that keep surviving get promoted to old regions, and old occupancy climbs. When it crosses the IHOP threshold (adaptive by default), G1 kicks off a concurrent marking cycle. It begins with a brief stop-the-world initial mark (piggybacked on a young collection) that snapshots the roots, then marks the live graph concurrently while the application runs. The SATB write barrier records any reference the application overwrites during marking, so an object reachable at the snapshot is never missed even if the app unlinks it mid-cycle. A short remark pause finishes processing the SATB queues, and a cleanup phase computes per-region liveness and identifies the emptiest old regions.

Now G1 has what it needs for mixed collections. Over the next several young pauses, each collection's collection set includes not just eden and survivors but a batch of the garbage-heaviest old regions the pause predictor says fit within the pause budget. Live objects in those old regions are evacuated and compacted into fresh old regions; the garbage-dominated source regions are freed. Spreading old-region reclamation across many bounded pauses — instead of one giant full-heap compaction — is exactly how G1 keeps individual pauses under the target while still reclaiming the old generation.

Consider the unhappy path. Suppose allocation spikes and the application promotes objects faster than marking and mixed collections can reclaim them. Old regions fill; G1 tries to evacuate a young collection but there are no free regions to copy survivors into — an evacuation failure. G1 falls back, and if it still cannot make progress it triggers a full GC: a stop-the-world collection and compaction of the entire heap, single-digit-second pause on a large heap. This is the pathological pause G1 was built to avoid, and every operational lever — larger heap, earlier IHOP, faster marking, fewer humongous allocations — exists to keep the service out of it.