Skip to content

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.

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:

# application.yml
spring:
  threads:
    virtual:
      enabled: true

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