Golang - Garbage Collection
Go uses a tracing garbage collector based on a concurrent, tri-color mark-and-sweep algorithm.
Designed for low latency, Go’s collector runs concurrently with application goroutines (mutators), keeping Stop-The-World (STW) pauses under a fraction of a millisecond.
The Tri-Color Marking Abstraction
During the marking phase, heap objects are conceptually partitioned into three sets (colors):
- White: Unvisited objects. At the end of the mark phase, white objects are unreachable garbage and will be swept.
- Grey: Reachable objects whose references haven't been fully scanned yet.
- Black: Reachable objects whose references have been completely scanned. Black objects are proven reachable and cannot point directly to white objects without going through a grey object.
Phases of a GC Cycle
A single garbage collection cycle flows through four distinct steps:
[ Running (Off) ] ---> [ STW: Mark Setup ] ---> [ Concurrent Marking ]
|
[ Concurrent Sweeping ] <--- [ STW: Mark Termination ] <------+
Phase 1: Mark Setup (STW ~Sub-millisecond)
- Action: Brief Stop-The-World pause.
- Tasks: Enables the Write Barrier (to catch pointer updates made by user code during concurrent marking) and prepares GC worker background goroutines.
Phase 2: Concurrent Marking (~25% CPU target)
- Action: Program execution resumes alongside GC mark workers running on
g0stacks across dedicated P processors. - Tri-Color Flow:
- Root objects (stack pointers, global variables) are colored Grey.
- Workers pick a Grey object, scan its pointers, color referenced objects Grey, and promote the original object to Black.
- Repeats until the Grey set is empty.
Phase 3: Mark Termination (STW ~Sub-millisecond)
- Action: Second brief STW pause.
- Tasks: Disables the write barrier, flushes remaining local work queues, and calculates heap metrics for the next trigger point.
Phase 4: Concurrent Sweeping
- Action: Program runs fully.
- Tasks: Unmarked (White) memory spans are lazily reclaimed and returned to the heap allocator (
mheap) as goroutines allocate memory.
How Concurrency Is Kept Safe: Write Barriers
Because mutator goroutines run at the same time as the collector, application code could move a reference from a Grey object to a Black object and erase the link from the Grey object.
To prevent the collector from mistakenly sweeping active memory, Go uses a Dijkstra/Yuasa hybrid write barrier.
When a pointer assignment occurs during marking (*ptr = newObj), the write barrier intercept code shaded in runtime assembly ensures newObj is instantly colored Grey before the write finishes.
GC Pacing & Tuning (GOGC & GOMEMLIMIT)
Go triggers a GC cycle based on target allocations rather than fixed timers:
Live Heap + (Live Heap * GOGC / 100)
GOGC(Default =100): Defines the growth ratio before GC triggers. At100, if the live heap after a sweep is 100 MB, the next GC triggers when the heap hits 200 MB.GOMEMLIMIT(Go 1.19+): Sets a hard memory limit for the process. When memory usage approachesGOMEMLIMIT, the runtime automatically adjustsGOGCdynamically to prevent Out-Of-Memory (OOM) kills in containerized environments (like Docker or K8s).

