Under The Hood
backend go Go 1.21+ (concurrent GC)

Tri-color Mark-Sweep GC, Write Barriers, and GOGC Pacing

Last updated
Prerequisites:
Go memory model basics
Understanding of GC concepts (mark, sweep)
  • go
  • garbage-collector
  • tri-color
  • write-barrier
  • gogc
  • memory

Read at your depth

The practical view

Go's GC is a concurrent, tri-color mark-sweep collector. Colors: white (unvisited), grey (found but not yet scanned), black (fully scanned). GC runs concurrently with your program, with brief stop-the-world pauses for phase transitions. It's triggered by allocation pressure: the GOGC environment variable (default 100) sets the target heap growth — GC runs when the heap grows ~100% since the last cycle. Tune with GOGC=200 (less frequent, more memory) or GOGC=50 (more frequent, less memory), or set debug.SetGCPercent. Modern Go also has a soft memory limit via GOMEMLIMIT.

The same idea in other frameworks

react equivalent
N/A — Go runtime concern.

Legacy vs modern

Default GOGC vs tuned GOGC for a latency-sensitive service

Default GC (GOGC=100) balances CPU vs memory; a lower GOGC keeps memory down at the cost of more GC cycles.

before → after
Untuned default GC
// ❌ OLD: GOGC=100 default — GC pacing may cause latency spikes
GOMEMLIMIT + tuned GC percent
// ✅ NEW: soft memory limit + explicit pacing
// GOMEMLIMIT=512MiB GOGC=200 ./app

Interview gotchas

Context

This tests the concurrent-GC correctness mechanism — interviewers want the tri-color invariant and why the barrier exists, not just 'it's concurrent'.

The mechanical answer

During concurrent marking, the mutator (your program) continues running and can modify the object graph. The tri-color invariant requires that no BLACK (fully-scanned) object holds a pointer to a WHITE (unmarked) object — otherwise the GC could finish marking, consider the white object garbage, and free it while a live black object still points to it. The write barrier intercepts pointer stores: when a goroutine writes a pointer, the barrier records the write (in Go's case, the Dijkstra insert barrier marks the newly-referenced object grey, or the Yuasa deletion barrier re-scans during the scan phase). This guarantees the GC never frees a reachable object. Without the barrier, the GC would need to stop the world for the entire mark, destroying concurrency.

Trap

The naive answer is 'the write barrier makes GC faster'. It's a correctness mechanism for concurrent marking, not a speed optimization (it adds a small per-pointer-store cost). Another trap: assuming the heap is compacted — Go's GC is non-moving, so pointers stay valid and the barrier approach is safe. Candidates who state the tri-color invariant and the insert-barrier behavior show real GC understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.