Garbage Collector
What is Go Garbage Collector (GC)?
The Go Garbage Collector (GC) is a concurrent, tri-color mark-and-sweep collector designed for low pause times. It runs alongside your program with minimal disruption, freeing developers from manual memory management while remaining predictable and efficient. It is one of the central parts of the runtime. It automatically manages memory so you don't have to malloc/free manually, but it’s tuned for low pause times and concurrency.
GC model
Go uses a non-moving, concurrent, tri-color mark-and-sweep collector.
- Non-moving: Objects don't get relocated in memory (no compaction).
- Concurrent: GC runs in parallel with your program (mutator).
- Tri-color: Objects are classified as white, grey, or black while marking:
- White = unreachable (will be collected).
- Grey = discovered but not yet scanned.
- Black = reachable and fully scanned.
Phases of a GC cycle
- Sweep Termination
- Finalizes sweeping from the previous cycle.
- Ensures heap state is consistent before starting new marking.
- Mark
- Starts at the root set (globals, stacks, registers).
- Traverses pointers and marks reachable objects.
- Runs concurrently with your program.
- Uses a write barrier (mutator assist): when your code updates a pointer, runtime ensures GC doesn’t miss it.
- Mark Termination
- Short stop-the-world (STW) pause.
- Final marking to ensure no live objects are missed.
- Usually just a few hundred microseconds.
- Sweep
- Frees memory from unreachable (white) objects.
- Done incrementally in background goroutines.
- Each span (block of memory) is scanned and unused objects are returned to free lists.
Allocation behavior
- Allocations are fast: bump pointer within per-P caches (mcache).
- When caches are empty, they get replenished from central free lists.
- The allocator may trigger GC if heap grows faster than expected.
GC pacing
The runtime pacing algorithm balances GC frequency vs. memory overhead:
- Target heap growth factor ≈ 1.5× live heap (default, tunable via GOGC).
- Example: If live heap is 100 MB, next GC triggers at ~150 MB.
- Lower GOGC → more frequent collections, lower memory use, higher CPU.
- Higher GOGC → less frequent, more memory, lower CPU.
Latency & concurrency
- STW pauses are minimized (sub-millisecond in most programs).
- Most GC work is concurrent with goroutines.
- Goroutines may perform mutator assists (helping GC) during heavy allocation.
Finalizers
- You can attach a runtime.SetFinalizer().
- Runs after obj becomes unreachable, before memory is freed.
- Rarely used; can delay GC if misused.
Key properties
- Pros:
- Very low pause times → suitable for servers, real-time-ish workloads.
- Simple memory model for developers.
- Cons:
- Higher memory overhead (no compaction → fragmentation).
- Latency-sensitive apps (HFT, embedded) may still prefer manual memory pools.