Explain Go's concurrent tri-color mark-and-sweep garbage collector.
Go's GC is concurrent, non-moving, non-generational, tri-color mark-sweep. Objects are in one of three sets:
- White: not yet seen. Any object still white at the end of marking is garbage.
- Grey: reachable, but its children have not been scanned yet. These sit on the work queue.
- Black: reachable, and all of its children have been scanned.
Marking starts by greying the roots: stacks, globals and runtime structures. Workers repeatedly take a grey object, grey its white children, and blacken it, until no grey objects remain. White objects are then reclaimed by the sweep, which runs lazily as spans are reused and also in the background.
Because the program (the "mutator") keeps running during marking, it could hide a white object by storing its only pointer into a black object and deleting it from a grey one. The collector would then free live memory. The tri-color invariant prevents this: a black object may never point to a white object, or, in the weak form, only if that white object is still reachable from some grey object. The write barrier maintains the invariant.
Two more details: new objects are allocated black during marking, and goroutines that allocate quickly are made to do mark assists, which is proportional marking work, so they cannot outrun the collector.
What the interviewer is looking for: the invariant, why concurrent marking needs a barrier, and that pauses are short while CPU and throughput carry the cost.
More on Memory, GC & Runtime Internals
- Q334How do goroutine stacks grow, and why does that constrain the language?
- Q335Describe Go's memory allocator: size classes, mcache/mcentral/mheap, and the tiny allocator.
- Q337What is a write barrier and which one does Go use?
- Q338Walk through the phases of a GC cycle. Where are the stop-the-world pauses?
- Q339Is Go's GC generational or compacting? What is the "Green Tea" GC?
- Q340What does GOGC control, exactly? What happens with GOGC=off, 50 or 200?