JVM Tuning¶
Status: 🟢 Active | Owner: Java Guild
Defaults Are Usually Correct¶
Modern JVMs (Java 21) have excellent adaptive defaults. Do not tune JVM flags without a profiler-backed reason. Premature JVM tuning is a common source of production incidents.
Recommended Baseline Flags¶
The following flags are approved for all production services:
-XX:+UseG1GC
-XX:MaxRAMPercentage=75.0
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heapdumps
-XX:+ExitOnOutOfMemoryError
-Djava.security.egd=file:/dev/./urandom
Set these in the Dockerfile or JAVA_OPTS environment variable — never hard-code in application.yml.
ENV JAVA_OPTS="-XX:+UseG1GC \
-XX:MaxRAMPercentage=75.0 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/heapdumps \
-XX:+ExitOnOutOfMemoryError"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
Memory Sizing¶
Use -XX:MaxRAMPercentage instead of fixed -Xmx so the JVM automatically adapts to the container's memory limit:
| Container Memory | -XX:MaxRAMPercentage=75 heap | Overhead |
|---|---|---|
| 512 MiB | ~384 MiB | ~128 MiB (threads, metaspace, native) |
| 1 GiB | ~768 MiB | ~256 MiB |
| 2 GiB | ~1.5 GiB | ~512 MiB |
Always leave 20–25% headroom for non-heap memory (thread stacks, metaspace, GC).
Garbage Collector Selection¶
| GC | Status | When to Use |
|---|---|---|
G1GC (-XX:+UseG1GC) | ✅ Default | General purpose; balanced throughput and latency |
ZGC (-XX:+UseZGC) | ✅ Approved | Low-latency requirements (<10ms GC pauses); requires profiling justification |
| Shenandoah | ⚠️ Evaluate | Evaluate with Architecture Team |
| Serial / Parallel GC | ❌ Not approved | Production services only |
Virtual Threads (Project Loom — Java 21)¶
Spring Boot 3.2+ supports virtual threads. Enable for I/O-bound services:
With virtual threads enabled, remove any custom thread-pool sizing — the JVM manages carrier threads automatically. Do not mix virtual threads with thread-local-based frameworks that assume platform threads.
Diagnosing Memory Problems¶
# Take a heap dump from a running pod
kubectl exec <pod-name> -- jcmd 1 GC.heap_dump /tmp/heap.hprof
kubectl cp <pod-name>:/tmp/heap.hprof ./heap.hprof
# Analyse with Eclipse MAT or JDK Mission Control
References¶
Last reviewed: 2025-Q4 | Owner: Java Guild