Architectural Comparison with C4/Pauseless?

Gil Tene gil at
Wed Dec 13 06:19:12 UTC 2017

To Per and the rest of ZGC team: Congratulations on having the ZGC project and sources up! It is great to see another concurrent compacting collector being actively developed.

As I start digging into some of the ZGC details, it seems (at least at first pass) that we are looking at a main mechanism that is very similar to C4 (2011 ISMM paper here:, or the single-generation Pauseless collector that preceded it (2005 VEE/Usenix paper here: This suggests that much of the JVM infrastructure and related design needs will end up being similar as well, and we can both benefit from understanding those similarities and comparing notes.

Can we go through a quick stab at mapping the mechanisms and terminologies?

Some high level similarities I've noted so far:

- The use of a barrier-at-refernce-load that determines whether or not an action is required purely based on the contents of the reference being loaded and some expected values for that contents (as opposed to considering data that would require de-referencing through the pointer): This seems equivalent to what the C4 LVB does [are we looking at the same actions? i.e.: queue to collector if not-yet-marked-through, fixup/remap to point to actual target address if points-to-relocated-object, and relocate object if points-to-needs-to-be-relocated-but-not-yet-relocated object?].

- Colored pointers in ZGC (which appear to encode metadata in the pointer): These seem similar to the concept of using metadata information in the reference in C4 and pauseless [A combination of NMT state and page numbers or ranges], including the use of similar triggering reasons (not-yet-marked-through, points-to-relocated-object, and points-to-obejct-that-needs-to-be-relocated] and their use in marking, compaction, and eventual fixup.

[Do you see fundamental differences here that go beyond representation of the metadata in the pointer field? Are there any key differences in the triggering reasons or in how they are used to create the concurrent mark, relocate, and eventual fixup passes (or their equivalent terms in ZGC if there is a logical match)? ]

- Use of a common barrier test for both concurrent marking and concurrent compaction in ZGC: Same as C4 LVB. [Basically the "only leave the fast path if metadata shows that there is something to be done" test].

- "In-place compaction" in ZGC: Same as C4's Quick Release. (Basically releases and recycles compacted-from pages[/regions] before fixing up references to objects in those pages, by maintaining forwarding information outside of the object body and page)

These obviously lead me into a trap of assumptions (since I keep thinking in C4 terms).

Question about expected invariants and reference fixup:

- Do the colored pointers and the related read barrier provide invariants similar to those described in C4 (section 2.1)? Can similar assumptions be made about mutator visible references?

- Does ZGC fold a "fixup phase" (aka "remap phase" in C4) of references to compacted pages into the next Mark phase (delaying the complete fixup until then)? Or does it perform a separate fixup pass after relocation?

- "Self healing": I didn't see mention of a C4/Pauseless "self healing" equivalent thus far in text, and have not followed the ZGC code far enough to determine if there is one. Does "ZGC" perform self-healing on references that were found to need attention by the barrier?

Questions about pages/regions/boundaries/mapping:

- Is ZGC "regional" In the sense that except for large objects (that span multiple dedicated regions) objects cannot span region boundaries? [This is the case in C4, Shenandoah, and G1], or does it handle the heap in some more fluid way?

- Does ZGC use regions of fixed size ("ZPage"?) when those regions are not dedicated to single (large) object?

- Does ZGC use virtual memory remapping to relocate "large" (larger than a single page) objects?

- Does ZGC use virtual memory remapping on "normal" (non-large-object) regions as part of preparing or performing marking? Compaction?

Question about object lifecycle and pipeline:

In gaining an understanding for pretty much any collector, I usually find that gaining an understanding for what happens to an object and references to it in two main (fairly simple) scenarios really helps. To help with that, can you describe the high level pipeline in these two simple cases [lets focus on "non-large" objects, assuming larger-than-one-page objects differ in some way (which may be a wrong assumption on my part)]

- For a "stays alive through one collection object: From allocation (presumably in a TLAB), through preparation for marking and marking, and then through preparation for relocation and the relocation, and then through having references to the object fixed up.

- For a "died quickly and gets allocation in the next collection" object: From allocation (presumably in a TLAB), [assume death here] through preparation for marking and marking, and then through preparation for relocation of other objects allocated in the same region, the relocation of those objects, and the eventual "release" [freeing/recycling/whatever] of the region that the dead object used to be in.

Anyway, the above is plenty (probably too much) for a single mailing list thread, so I'll stop there.

— Gil.

More information about the zgc-dev mailing list