Introduction
Go's garbage collector runs concurrently with your application. While it requires minimal tuning for most workloads, high-throughput services benefit from understanding GOGC, GOMEMLIMIT, and the new Green Tea GC algorithm that became the default in Go 1.26.
Key Concepts
- GOGC: Controls GC frequency. Default is 100 (GC triggers when heap grows 100% since last GC). Higher values = less frequent GC, more memory.
GOGC=offdisables GC. - GOMEMLIMIT: A soft memory limit (e.g.,
GOMEMLIMIT=4GiB). The GC runs more aggressively as the program approaches this limit, preventing OOM kills. - Green Tea GC (Go 1.26 default): A new GC algorithm that reduces garbage collection overhead by 10-40% in real-world programs. It improves marking and scanning of small objects through better locality.
- GC pauses: Brief stop-the-world pauses at the start and end of each GC cycle. Typically < 1ms in Go 1.26.
Real World Context
A batch processing service with 32 GB of RAM running at GOGC=100 wastes half its time in GC. Setting GOMEMLIMIT=28GiB with GOGC=off lets the heap grow freely until it approaches the limit, then GC kicks in — cutting GC CPU overhead from 25% to under 5%.
Deep Dive
GOGC: Frequency Control
bash# Default: GC when heap doubles GOGC=100 ./myapp # Less frequent GC, higher memory usage GOGC=200 ./myapp # Disable automatic GC (must use GOMEMLIMIT or manual runtime.GC()) GOGC=off ./myapp
GOGC is a ratio: if the heap was 100 MB after the last GC, and GOGC=100, the next GC triggers at 200 MB.
GOMEMLIMIT: Memory-Aware GC
Introduced in Go 1.19, GOMEMLIMIT adds a memory ceiling:
bashGOMEMLIMIT=4GiB ./myapp
When the heap approaches the limit, the GC runs more frequently regardless of GOGC. This prevents OOM kills in containerized environments where you know exactly how much memory is available.
The recommended pattern for containers:
bash# Container has 4 GB — reserve 500 MB for non-heap (stacks, goroutines, OS) GOMEMLIMIT=3500MiB GOGC=100 ./myapp
Green Tea GC (Go 1.26)
Go 1.26 makes the Green Tea GC the default (was experimental in Go 1.25). Key improvements:
- 10-40% reduction in GC overhead: Improved marking and scanning of small objects through better locality and CPU scalability.
- Vectorized scanning on modern CPUs: ~10% additional improvement on newer amd64 CPUs (Intel Ice Lake+, AMD Zen 4+) via vector instructions.
- Same API: No code changes needed — you get the improvements just by upgrading to Go 1.26.
- Opt-out: Set
GOEXPERIMENT=nogreenteagcif needed (expected to be removed in Go 1.27).
Programmatic Control
goimport "runtime/debug" // Set GOGC programmatically debug.SetGCPercent(200) // Set memory limit programmatically debug.SetMemoryLimit(4 << 30) // 4 GiB
Common Pitfalls
- Setting GOGC too low — GOGC=10 runs GC constantly, wasting 30-50% of CPU on garbage collection.
- GOGC=off without GOMEMLIMIT — The heap grows unbounded until the OS kills the process. Always pair GOGC=off with GOMEMLIMIT.
- Setting GOMEMLIMIT too close to container limit — Leave headroom for goroutine stacks and OS overhead. Use 80-90% of container memory.
Best Practices
- Use GOMEMLIMIT in containers — Set it to 80-90% of the container's memory limit. This gives the GC a clear target.
- Benchmark before tuning — Profile GC overhead with
GODEBUG=gctrace=1before changing GOGC. Many apps need no tuning.
Summary
- GOGC controls how often GC runs (default 100 = heap doubles between cycles).
- GOMEMLIMIT caps memory usage, preventing OOM in containers.
- Go 1.26's Green Tea GC reduces GC overhead by 10-40% with no code changes.
- Pair
GOGC=offwithGOMEMLIMITfor maximum throughput in memory-constrained environments. - Always measure GC overhead before tuning — use
GODEBUG=gctrace=1.
Code Examples
package main
import "runtime/debug"
func tuneGC() {
// Approach 1: Reduce GC frequency for throughput
debug.SetGCPercent(200) // GC when heap triples
// Approach 2: Memory-bounded (ideal for containers)
debug.SetGCPercent(-1) // Disable GOGC-based triggering
debug.SetMemoryLimit(3500 << 20) // 3.5 GiB soft limit
// The GC now runs only when approaching the memory limit,
// maximizing throughput while preventing OOM.
}