Walk through the phases of the HotSpot G1 collector's concurrent marking cycle, from what triggers it to what it produces, and explain how its output is used afterwards.
answer
- IHOP triggers, occupancy-based
- initial mark rides a young pause
- root region scan = survivors as roots
- remark drains barrier backlog
- cleanup frees empty regions, ranks candidates
basics
~20 sHeap occupancy crossing a threshold starts the cycle: initial mark (piggybacked on a young pause), concurrent root-region scan, concurrent mark, a short stop-the-world remark, then cleanup. It produces per-region live-data counts, frees entirely empty regions immediately, and builds the old-region candidate list for mixed collections.
solid answer
~60 sThe cycle starts when heap occupancy reaches the initiating threshold - historically `-XX:InitiatingHeapOccupancyPercent` (45 by default), adaptively tuned in modern JDKs. 1. **Initial mark** - stop-the-world, and piggybacked on an ordinary young evacuation pause so it costs almost nothing extra. It marks the objects directly reachable from roots. 2. **Root region scan** - concurrent. The survivor regions produced by that young pause are scanned for references into old regions; it must finish before the next young collection can evacuate those survivors. 3. **Concurrent mark** - concurrent traversal of the object graph, counting live bytes per region as it goes. 4. **Remark** - stop-the-world. It drains the pending write-barrier buffers and finishes the marking that concurrent mutation left outstanding, giving a consistent live set. Reference processing and class unloading happen here. 5. **Cleanup** - mostly stop-the-world but brief. It finalises per-region live counts, reclaims fully empty regions immediately without any copying, and sorts old regions into the mixed-collection candidate list. Marking never frees non-empty regions itself; it produces the liveness data that later mixed collections act on.
code
text · 8 lines[gc] Pause Young (Concurrent Start) (G1 Evacuation Pause) 4096M->2810M(8192M) 21.4ms
[gc] Concurrent Mark Cycle
[gc] Concurrent Scan Root Regions 3.9ms
[gc] Concurrent Mark 412.6ms
[gc] Pause Remark 2810M->2795M(8192M) 8.7ms
[gc] Pause Cleanup 2795M->2101M(8192M) 1.2ms <- empty regions freed with no copying
[gc] Concurrent Mark Cycle 431.0ms
[gc] Pause Young (Mixed) (G1 Evacuation Pause) 2101M->1204M(8192M) 34.8msgo deeper
Recall that G1 marks live objects concurrently with the application and that a couple of short stop-the-world phases bracket the concurrent work.
Name the phases in order, note that initial mark rides a young pause, and state that the output is per-region liveness plus immediate reclamation of empty regions.
Tie the cycle to operations: what the initiating threshold controls, why remark length tracks mutation volume, why root-region scan can delay a young pause, and how a late cycle leads to evacuation failure.
Discuss the concurrency budget - marking threads compete with application threads for CPU - and how cycle duration, allocation rate, and heap headroom form the stability condition for a latency-sensitive service.
## What the cycle is for G1 reclaims old regions by evacuation, and to evacuate profitably it must know how much live data each old region holds. Nothing in a young collection reveals that. The concurrent marking cycle exists to compute per-region liveness for the whole heap while the application keeps running, so that later mixed collections have a ranked candidate list to draw from. ## What starts it G1 watches total heap occupancy, including humongous regions. When it reaches the initiating heap occupancy percent - `-XX:InitiatingHeapOccupancyPercent`, historically fixed at 45% - a cycle is requested and begins at the next young pause. Modern HotSpot adapts this threshold at run time based on how much allocation happens during previous cycles, so the static flag is a starting point rather than a constant. The tuning consequence is that the cycle must be started early enough to finish before the heap fills; starting too late is the classic cause of evacuation failure. ## Phase by phase **Initial mark (stop-the-world).** Marks objects directly reachable from GC roots. G1 does not schedule a separate pause for it: it rides along on a normal young evacuation pause, which already stops the world and already walks roots. In logs it appears as a young pause annotated as an initial-mark or concurrent-start pause. **Root region scan (concurrent).** The young pause that carried initial mark produced survivor regions full of freshly copied objects. Those survivors may contain references into old regions that marking must follow, so they are treated as additional roots and scanned concurrently. This scan must complete before the *next* young collection, because that collection would evacuate the survivors out from under it. A long root-region scan can therefore delay a young pause. **Concurrent mark (concurrent).** Worker threads traverse the reachable object graph alongside the application, accumulating live-byte totals per region. This is where the bulk of the time goes on a large heap. Because the application is mutating references at the same time, marking uses a snapshot-based scheme so that objects live at the start of the cycle are not lost when references are overwritten; the write-barrier machinery that makes this sound is the general marking-correctness mechanism rather than something specific to G1's region design. **Remark (stop-the-world).** Concurrent marking leaves a backlog: buffers of references recorded by the barrier while marking ran. Remark stops the world, drains that backlog, and completes marking to a consistent state. Reference processing (soft/weak/phantom handling) and class unloading are done here. Remark is short but is a real pause, and its length grows with the volume of concurrent mutation. **Cleanup (mostly stop-the-world, brief).** Finalises per-region live counts, sorts old regions by liveness into the candidate list for mixed collections, resets marking data structures, and - importantly - immediately frees any region found to contain no live objects at all. Freeing a completely empty region needs no copying, so this reclamation happens without evacuation. Entirely dead humongous regions are typically recovered here too. ## What happens next After cleanup, G1 begins mixed collections: young evacuations that also include old candidate regions chosen richest-in-garbage first. They continue until the candidate list is exhausted, the reclaimable fraction falls below `-XX:G1HeapWastePercent`, or a new marking cycle is needed. Then G1 returns to young-only collections until occupancy again crosses the initiating threshold. ## The failure mode to name If allocation is fast relative to the cycle's duration, the heap fills before marking finishes and before mixed collections can reclaim. G1 then has no free regions to evacuate into, hits evacuation failure (to-space exhaustion), and in the worst case falls back to a full compacting collection. The correct responses are to start the cycle earlier, give the concurrent workers more threads, or enlarge the heap - not to shorten the pause goal, which makes G1 collect less per pause.
- Why does the initial-mark phase not appear as its own pause in the logs?Because it is piggybacked on a normal young evacuation pause, which already stops the world and already scans GC roots. Reusing that pause avoids a second safepoint and the duplicated root scan. The log therefore shows a young pause tagged as concurrent-start or initial-mark rather than a standalone marking pause.
- Some regions are freed during Cleanup even though no evacuation happened. How?Marking discovers that those regions contain no live objects at all. A completely dead region needs nothing copied out of it, so it can be returned to the free list directly. This is why heap occupancy can drop noticeably at Cleanup, and it is the cheapest reclamation G1 performs.
saying these in an interview costs you the question
- Saying the concurrent marking cycle frees memory by sweeping - it computes liveness; only completely empty regions are reclaimed during Cleanup.
- Claiming the whole cycle is fully concurrent with no pauses; initial mark, remark, and cleanup are safepoints.
- Confusing a mixed collection with a phase of the marking cycle - mixed collections happen after it, using its output.
- Believing the cycle is triggered by allocation rate or by a timer rather than by heap occupancy crossing the initiating threshold.
- Recommending a lower pause-time goal to fix a cycle that finishes too late; that reduces work done per pause and makes matters worse.