The mark phase is the trace, colored. Today you build it - grey the roots, then repeatedly take a gray object, grey its white children, and blacken it - until every reachable object is black.
Blacken exactly the reachable objects by draining a gray worklist from the roots.
The mark phase is the reachable-set trace from the last chapter, expressed in colors. Start by greying every root - they are reachable by definition and their children are not yet scanned. Then drain a worklist of gray objects: pop one, look at each child, and grey any child that is still white (newly discovered); once its children are all greyed, paint the object black. Repeat until no gray remains.
Greying a child only if it is white is what makes this terminate and handle sharing
and cycles for free - an object already gray or black is not re-added, exactly like
the seen guard before. When the worklist empties, black is the reachable set and
white is the garbage: every live object the roots can reach is black, and
everything the trace never touched is still white. That white-versus-black split is
the entire decision the sweep will act on next. The gray color is transient - it only
exists while the phase runs.
func (h *Heap) Mark() {var gray []Reffor _, r := range h.Roots() { h.SetColor(r, Gray); gray = append(gray, r) }for len(gray) > 0 {r := gray[len(gray)-1]; gray = gray[:len(gray)-1] // popfor _, c := range h.Children(r) {if h.Color(c) == White { h.SetColor(c, Gray); gray = append(gray, c) }}h.SetColor(r, Black)}}