JVM Garbage Collection Pauses
Diagnose and reduce JVM GC pause times that cause latency spikes and health check failures.
Overview
Overview
JVM garbage collection pauses are one of the most common causes of latency spikes in Java, Kotlin and Scala services. During a Stop-The-World pause, the JVM suspends every application thread to find and reclaim unreachable objects, so from the caller's side the service simply stops responding.
Choosing a collector
- Parallel GC — -XX:+UseParallelGC — Throughput; the Java 8 default — Multiple GC threads, but stops the world for all major collections
- G1GC — -XX:+UseG1GC — Balanced throughput and latency; the default since Java 9 — Splits the heap into regions, collects the most garbage-dense first, targets -XX:MaxGCPauseMillis=200
- ZGC — -XX:+UseZGC — Available since Java 11, production-quality since Java 15 — Concurrent, colored pointers; sub-millisecond pauses even on heaps of hundreds of gigabytes
- Shenandoah — Red Hat and OpenJDK builds — Concurrent compaction, low pauses — Similar goals to ZGC, different implementation trade-offs
Checks and what each tells you
- -Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=20m — When each pause happened, how long it lasted, how much was reclaimed and which phase caused it; rolls at 20MB, keeps 5 files
- GCEasy (gceasy.io) — Pause-time distribution, heap use over time, tuning recommendations from an uploaded log
- GCViewer — The same log, analyzed in an open-source desktop tool
- Micrometer jvm.gc.pause metric with the cause tag — GC pauses in Prometheus, next to your latency graphs
Heap sizing mechanics
> [!TIP] For G1GC, aim for 60-70% heap occupancy after each major GC; that leaves headroom for the next allocation wave.
Containers: always set -Xmx
> [!IMPORTANT] Always set -Xmx explicitly in containers. Java 10+ is container-aware, but older JVMs without -XX:+UseContainerSupport size the heap from host RAM and get OOM killed.
Key concepts
- Stop-The-World Pause
- A GC event that suspends all application threads while the collector performs its work — the direct cause of latency spikes observed by callers.
- G1GC
- The default JVM GC since Java 9 — divides heap into regions, collects highest-garbage regions first, and targets a configurable pause time goal.
- ZGC
- A concurrent garbage collector (Java 15+ production) that achieves sub-millisecond pause times on heaps up to 16TB by performing most GC work concurrently with the application.
- -Xmx
- JVM flag setting the maximum heap size — must be set explicitly in containers to prevent the JVM from using host RAM as the reference for auto-sizing.
- GCEasy
- A web-based GC log analyzer that parses JVM GC logs and produces pause time distributions, heap utilization charts, and tuning recommendations.
When to use it
- Diagnosing periodic latency spikes in a Java service that correlate with GC events
- Choosing between G1GC, ZGC, and Shenandoah for a specific latency and heap size requirement
- Analyzing GC logs to identify the phase causing long pause times
- Explaining JVM heap sizing for container environments to avoid OOM kills
- Setting up JVM GC metrics in Prometheus for continuous GC pause monitoring
Interview questions
- How do you enable and analyze GC logs to identify the cause of a latency spike?
- How should you size JVM heap for a containerized Java application to avoid both GC overhead and OOM kills?
- When should you choose ZGC or Shenandoah over G1GC?
Sign up free to read the full topic
More Troubleshooting topics
- EC2 Instance Unresponsive
- ECS Task Failures and Restarts
- ELB 5xx Errors
- Flaky CI/CD Pipeline Failures
- HPA Not Scaling
- IAM Permission Denied Errors
- Kubernetes Service Not Reachable
- Lambda Cold Start Latency
- Log Volume Explosion and Cost Runaway
- Memory Leak Diagnosis
- Metrics Gaps in Dashboards
- Missing Traces in Distributed Tracing
Start free on Cariara · Coding problems · System design studio