← Concepts

阅读中文版本

GC Pauses and Stop-the-World

GC pauses are the intervals the runtime stops application threads to enumerate and move objects; their length and frequency, governed by collector choice and allocation rate, set latency floors.

runtime

Definition

Garbage collectors reclaim memory by tracing live objects, which requires application threads to pause at safepoints — stop-the-world phases — for root scanning and some compaction. Collectors trade pause length against throughput: young-generation collectors pause briefly and often; concurrent collectors shrink pauses at the cost of CPU overhead and pacing.

Context and scope

Tuned on JVM services (G1, ZGC, Shenandoah), Go services (STW under a millisecond), and .NET workstations. Allocation rate is the input that dominates pause behavior.

Common misconceptions

  • Concurrent collectors eliminate pauses — they shrink but do not remove them; root scanning and relocation phases still stop threads
  • Pauses depend only on heap size — they depend on live set, reference density, and allocation rate; a small hot heap can pause harder than a large cold one
  • GC is a background problem — allocation pressure is a foreground latency input: every temporary object feeds the next cycle

Anti-patterns

  • Allocating per-request wrappers, buffers, and DTOs at high rates and driving young collections into every response tail
  • Tuning collector flags on synthetic benchmarks and shipping settings that collapse on production live-set shapes
  • Ignoring safepoint reachability: long-counted loops without polls stretch even tiny pauses into hundreds of milliseconds

Related concepts

Authoritative source

https://go.dev/blog/go15gc

What it is

Tracing collection must see a consistent object graph, so threads stop at safepoints while roots are scanned and, for compacting collectors, objects move. Pause time distributions — not averages — set the latency floor for interactive services.

How it works

Young collections copy survivors between spaces, pausing in proportion to live young objects. Concurrent old-generation collectors mark while the app runs, then take brief pauses for remark and relocation: ZGC and Shenandoah aim for sub-millisecond pauses on large heaps at CPU overhead cost. Go collectors cap STW to two narrow windows per cycle and pace assists by allocation rate.

When to tune

After measuring: pause logs and allocation profiles first, flags second. Reduce allocation rate — pooling hot objects, primitive collections, buffer reuse — before selecting an exotic collector. Match collector to SLA: throughput batch versus p99-bound API.

Pitfalls

Humongous objects skip young spaces in some collectors and destabilize old-generation pacing. Concurrent collectors fail their promise under allocation bursts that outrun marking — allocation pacing back-pressures the application instead. Safepoint bias: a JVM flag tuning guide from a different workload misleads more than it helps.