In Lesson 24, G1 marked old-generation liveness while application threads kept running. A collection set later uses that liveness to choose useful old regions. But how can the marker follow a graph that the application is changing at the same time?
Suppose marking begins with Root → A → B. The collector reaches A, but before it reads A.child, an application thread executes A.child = C. A marker that only follows the current edge can reach C and miss B, even though B was reachable when marking began.
A logical snapshot of beginning-of-cycle reachability
G1 uses Snapshot-At-The-Beginning (SATB). Its marking result conservatively treats objects live at the start of the cycle as live for that cycle. “Snapshot” describes the liveness view; G1 does not copy the entire heap into a second heap. The real heap keeps changing while marking data and reference-write barriers preserve information the marker needs.
SATB protects the old reference that is about to disappear while marking is in progress.
In the example, that reference is B. The assignment creates a new edge to C, but deletion of the old A → B edge could hide a snapshot-live object. This is the reason to inspect the old value before replacing it.
What the pre-write barrier records
Conceptually, the write has this order:
old = A.child // B
SATB pre-write barrier: preserve old when needed
A.child = C
The pre-write barrier is conditional collector bookkeeping around a reference store during active SATB marking. For a relevant non-null old reference, HotSpot can place that reference in a thread-local SATB buffer before the new store is installed. The diagram shows the logical sequence; it is not a Java-level implementation of every generated barrier fast path.
A small action on the mutator thread records marking work. Each thread can collect entries in its own SATB buffer, allowing collector work to be processed in batches. Enqueuing B preserves work for the marker; it does not mean B and everything reachable from B are already marked. The marker must process B and follow its references. If B leads to Q → R, those objects may become additional work.
The other G1 barrier watches the new relationship
Lesson 23 introduced card and remembered-set bookkeeping for partial collections. Its post-write barrier observes the newly installed reference relationship, marks a card when needed, and helps G1 maintain approximate incoming-reference information for a collection set. SATB asks which old reference is disappearing; card bookkeeping asks what new relationship may need remembering. A card is a coarse heap location, not an SATB queue entry.
Allocations after marking starts: TAMS
An object allocated after the marking snapshot did not exist in the beginning-of-cycle graph. G1 must not treat its absence from that traversal as proof that it is garbage. HotSpot keeps a per-region Top At Mark Start (TAMS) boundary: a conceptual dividing line between objects already present when marking began and allocations made afterward in that region. Newly allocated objects are handled conservatively for the current cycle. The diagram describes the boundary, not a promise that every region is a static stack of objects throughout every pause.
The cost of preserving beginning-of-cycle liveness
Imagine B was live at mark start, then A.child = null makes it unreachable. SATB may still preserve that old reference and treat B as live for this cycle. This floating garbage occupies space until a later marking cycle can recognize it as dead, subject to G1’s other reclamation paths.
Mutation and allocation continue while G1 traces. Heavy reference churn can add SATB work, and rapid promotion or allocation can raise old occupancy before marking completes. G1 therefore needs enough free heap and old-generation headroom to finish marking and reclaim space before allocation pressure becomes critical. A late start or insufficient marking capacity can make the cycle fall behind; investigate the actual logs before changing a threshold or heap size.
Where Remark fits in the cycle
The high-level sequence is Concurrent Start → Concurrent Mark → Remark → Cleanup. Concurrent Start is a young pause that starts the cycle. Most graph traversal takes place during Concurrent Mark while mutators run; ordinary young collections can also occur then. Remark is a stop-the-world finishing point, and Cleanup prepares the result for subsequent reclamation decisions.
At Remark, G1 finalizes outstanding marking and SATB work and performs related reference processing, class unloading, and cleanup. Draining a buffer is not enough if its B entry leads to unprocessed Q → R. Marking must reach a fixed point: relevant SATB entries and the graph work they generate have been processed so no reachable work remains pending for this cycle. Remark finishes a concurrent traversal; it does not start a whole-heap mark from scratch.
Start diagnosis with -Xlog:gc* to identify Concurrent Start, Concurrent Mark, Remark, Cleanup, Young, Mixed, or Full GC events and their timing. For evacuation phase costs, -Xlog:gc+phases=debug can expose root scanning and Object Copy. Correlate repeated cycles with old occupancy and available destination space: marking falling behind and evacuation running out of room are related pressure signals, but they require different explanations.
Check your reasoning
At mark start A points to B. The application replaces B with C before the marker reads the field. Which value matters to SATB, and why?
B is the old value whose edge is disappearing. Preserving it gives the marker a way to discover an object that belonged to beginning-of-cycle reachability. C is the new relationship; card bookkeeping addresses that separate concern.
The pre-barrier enqueues B. Is B now fully marked?
No. The buffer preserves a reference as marking work. Processing B may reveal further objects; marking completes only when the relevant queued and discovered work has been drained to a fixed point.
B becomes unreachable just after Concurrent Start. Why might old-space reclamation still count it as live?
It was live in the SATB snapshot. Conservative retention can leave floating garbage until a later cycle, even though the application no longer reaches B.
A log shows a long Remark and a separate long Mixed pause. Would one CSet-size explanation cover both?
No. Remark finalizes marking and related work; Mixed evacuation scans roots and remembered information and copies live objects. Inspect their phase evidence separately.
The combined G1 barrier model
SATB’s pre-write barrier preserves old disappearing references; the post-write card barrier records new relationships for remembered-set maintenance. Together they let G1 mark a changing graph and evacuate selected regions using incoming-reference information.
Lesson 26 will continue with G1 failure modes: evacuation failure, humongous objects, marking that falls behind, Full GC, and the headroom that connects them.
Sources and implementation boundaries
Based on Daily JVM Byte #25 and its reviewed draft. Object names, timelines, and diagrams are illustrative; no GC workload was run. SATB, TAMS, and barrier details describe HotSpot G1 in JDK 25 rather than Java language guarantees or every collector. Exact barrier paths and phase costs depend on runtime state and configuration.