JVM guide

JVM Flags: The Practical Tuning Guide

Every JVM behavior you can influence — heap size, collector choice, GC logging, JFR — is a command-line flag or a runtime-mutable flag. The painful part is that most tuning advice is folklore, so this guide focuses on the flags you will actually set, how to see what's in effect, and how to change them on a running process.

Modern Java (9+) also has Unified Logging (-Xlog) and, since JDK 11, flags you can flip live with jinfo and jcmd -XX external commands — but never confuse 'set a flag live' with 'tune correctly.' Measure before and after.

The flags you'll actually set

These cover 95% of production tuning. Get these right before touching exotic -XX flags.

java -Xms2g -Xmx2g \          # initial & max heap
     -XX:+UseG1GC \            # collector (G1 default in LTS 11/17/21)
     -XX:MaxMetaspaceSize=512m \ # bound metaspace
     -Xlog:gc*:file=gc.log:time,level,tags \ # GC log (JDK 9+)
     -jar app.jar
FlagWhat it doesCommon value
-Xms / -XmxInitial / max heap (set equal to avoid resize)equal, e.g. -Xms2g -Xmx2g
-XX:MaxMetaspaceSizeCap the (formerly permanent-gen) metaspace256m-512m
-XX:+UseShenandoahGC / UseZGCLow-pause collectors (JDK-specific availability)per workload
-XX:MaxGCPauseMillisG1 adaptive pause target (soft goal)200
-XX:+PrintGCDetails / -Xlog:gcGC loggingon for diagnosis

See what a JVM actually started with

Write good flags in the run script and verify with the runtime — not memory.

# Effective flags of a live JVM
jcmd <pid> VM.flags
jinfo -flags <pid>

# Just the GC collector / max heap
jcmd <pid> VM.flags | grep -iE 'UseG1|MaxHeap|Metaspace'

Change flags on a running JVM

Some flags are manageable at runtime via jinfo -flag and jcmd; others require a restart. Always confirm the flag is manageable before relying on a live tweak.

# Toggle a boolean or set a string flag live
jinfo -flag +PrintGC <pid>
jcmd <pid> VM.set_flag MaxGCPauseMillis 150

Tuning workflow (not folklore)

Baseline first, then change one variable. GC logging + a profiler give you before/after numbers.

# 1) baseline with GC log
java -Xlog:gc*:file=gc-baseline.log:time,level,tags -jar app.jar
# 2) profile hot methods
# 3) change ONE flag, repeat, compare

Flags to be careful with

Avoid cargo-cult -XX flags like -XX:+UseConcMarkSweepGC (removed in JDK 14). Prefer the defaults unless you have a measured reason. -XX:+TieredCompilation, -XX:+UseZGC etc. all have trade-offs.

Frequently asked questions

Should -Xms equal -Xmx?

Setting them equal avoids heap-grow/shrink resizes and pauses, and is standard for stable services. If you want headroom for spikes, a separate max can help but costs resize work.

Where do I put flags that changes are permanent?

In the JVM launch command (run script, container spec, or service manager's Java opts) — not only live via jinfo — so the config survives restarts and is reviewable.

Does GC flag tuning still matter with ZGC/Shenandoah?

Less so — low-pause collectors remove most 'tune G1 to reduce full GCs' drama. Tuning shifts to heap sizing, region sizing, and metaspace caps.

Last updated August 2026 · JVM Tools is independent and not affiliated with Oracle.