English
JVM Performance Tuning Guide
JVM tuning is not magic — it is a loop: measure → change one thing → verify. This guide gives you the workflow, the symptoms-to-fixes map, and the knobs that matter.
Step 1: Measure before you touch anything
Never tune blind. Enable GC logging (safe in production, < 3% overhead) and analyze a window that covers peak traffic:
bash
# Java 9+
java -Xlog:gc*:file=gc.log:time,uptime -jar app.jar
# Java 8
java -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log -jar app.jarThe report tells you which of the four common problems you actually have.
Step 2: Match the symptom to the fix
| Symptom in the report | Likely cause | What to try |
|---|---|---|
| Frequent Full GC | old gen too small / leak / oversized cache | Raise -Xmx; check for leaks (after-GC heap climbing); review cache sizes |
| High allocation rate | short-lived object churn | Object pooling or caching hot paths; reduce per-request garbage |
| Max pause > SLO | wrong collector or heap too big for collector | Switch to G1/ZGC; for G1 set -XX:MaxGCPauseMillis |
| Throughput < 90% | GC frequency too high | Bigger young gen (-Xmn / -XX:NewRatio); bigger heap |
| Metaspace GCs | classloader churn | Raise -XX:MaxMetaspaceSize; hunt dynamic proxies |
System.gc() in causes | explicit calls (RMI/NIO) | -XX:+DisableExplicitGC — verify Direct ByteBuffer reliance first |
| Humongous allocations (G1) | objects > half a region | Raise -XX:G1HeapRegionSize; refactor large arrays |
Step 3: Change one knob at a time
Tuning one thing, re-measuring, and comparing reports is the only way to know what worked. The knobs that cover 90% of cases:
| Knob | Effect |
|---|---|
-Xms = -Xmx | Fixed heap size — removes resize pauses, more predictable |
-Xmn / -XX:NewRatio | Young gen size — the main lever on GC frequency |
-XX:+UseG1GC / -XX:+UseZGC | Collector choice — the main lever on pause length |
-XX:MaxGCPauseMillis=200 | G1 pause target (don't set it unrealistically low) |
-XX:MaxMetaspaceSize | Class metadata cap |
-XX:+DisableExplicitGC | Ignore System.gc() calls |
Step 4: Verify and set a baseline
Re-analyze the new GC log and compare it with the old report: throughput up? max pause down? Full GCs gone? Save both reports (PDF/JSON) as your baseline — after the next release, one analysis tells you whether GC behavior regressed.
Checklist
TIP
New to the terminology? Start with JVM vs JRE vs JDK, then come back.
Related pages
- JVM Diagnostic Tools — jmap, jstack, jstat and Arthas by scenario
- JVM GC Metrics to Monitor — the numbers this workflow measures
- Enable GC Logging — the foundation of every analysis