GC Pause Tuning
GC pause tuning configures the collector to pause within the latency budget — sizing generations, choosing concurrent over stop-the-world, and trading throughput for pause duration.
Definition
GC pause tuning is the practice of configuring the garbage collector — generation sizes, collector algorithm, concurrent versus stop-the-world phases, and trigger heuristics — so that the pause durations fit within the application latency budget, accepting some throughput reduction for predictable latency.
Context and scope
Applied in JVM, Go, .NET, and any runtime with a tunable collector where the default pause profile does not meet the latency SLO.
Common misconceptions
- A concurrent GC eliminates pauses — it eliminates stop-the-world pauses for collection; synchronization points and the initital mark still pause, just briefly
- Bigger heap always means shorter pauses — it means fewer collections; each pause may be longer because there is more to scan
Anti-patterns
- Tuning the GC without measuring the actual pause distribution, optimizing against a guess
- Choosing the lowest-pause collector for a throughput workload, paying pause overhead it does not need
Related concepts
Authoritative source
https://engineering.linkedin.com/performance/optimizing-java-garbage-collection-tuning
What it is
GC pause tuning is the practice of configuring the garbage collector — generation sizes, collector algorithm, concurrent versus stop-the-world phases, and trigger heuristics — so that the pause durations fit within the application latency budget, accepting some throughput reduction for predictable latency. Applied in JVM, Go, .NET, and any runtime with a tunable collector where the default pause profile does not meet the latency SLO. Performance is a distribution, not a number: the tail determines user experience, and the mean hides the outliers that cause abandonment.
How it works
Measurement targets percentiles at the workload’s actual concurrency, not a single sequential run. The fast path is kept fast by deferring work that is not on it — lazy initialization, coalescing writes, avoiding allocation in hot loops. Profiling finds the bottleneck; micro-optimization without a profile optimizes the wrong thing. Constant factors matter, but algorithmic complexity dominates as scale grows.
When to optimize and pitfalls
Optimize when a measurement shows a concrete cost — latency budget burn, compute spend, user-visible jank. The pitfall is optimizing for the benchmark rather than the workload: a synthetic test that does not reproduce the production access pattern produces a local maximum that hurts globally. Another is trading memory for speed without accounting for the GC or eviction cost the extra allocation introduces.