Skip to content

JVM 启动参数速查 ​

参数不是背下来的,是出问题时知道该动哪一个。按"内存 / GC / 日志 / 容器 / 兜底"五组记。

一、内存 ​

bash
java -Xms4g -Xmx4g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
     -Xss512k -XX:MaxDirectMemorySize=1g -jar app.jar
参数管什么配错的后果
-Xms / -Xmx堆初始 / 最大两者不等会频繁扩容抖动;生产建议设成相同
-XX:MetaspaceSize触发 Full GC 的元空间阈值设太小会启动期频繁 Full GC
-XX:MaxMetaspaceSize元空间上限不设则只受物理内存限制,类泄漏会吃光内存
-Xss单线程栈大小调太小 → StackOverflowError;调太大 → 线程一多就 OOM
-XX:MaxDirectMemorySize堆外直接内存NIO/Netty 场景的隐形大户,OutOfDirectMemoryError 就是它

解释:-Xmx 之外还有堆外(元空间、线程栈、直接内存、JIT 代码缓存、GC 结构)。堆设 4G 的进程实际 RSS 到 5.5G 很正常——容器 limit 只按堆设就会反复被 OOMKilled。经验公式:容器 limit ≈ Xmx × 1.3 ~ 1.5。

二、GC 选择 ​

bash
# G1(JDK 9+ 默认):通用,停顿可控
-XX:+UseG1GC -XX:MaxGCPauseMillis=200

# ZGC(JDK 15+ 分代前是单代):大堆、超低停顿
-XX:+UseZGC -XX:+ZGenerational          # JDK 21+ 开分代

# Parallel:吞吐优先,批处理/离线任务
-XX:+UseParallelGC
场景选择理由
堆 < 8G,普通 Web 服务G1均衡,参数少
堆 ≥ 16G,对 P99 敏感ZGC停顿与堆大小基本无关
跑批、离线计算Parallel吞吐最高,不在乎单次停顿
CMS已删除JDK 14 起移除,遇到残留配置直接换 G1

三、GC 日志(JDK 9+ 统一格式) ​

bash
-Xlog:gc*:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=5,filesize=50m

JDK 8 的等价写法:

bash
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime \
-Xloggc:/var/log/app/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=50M

解释:filecount/filesize 是轮转,不加这行 GC 日志能写到几个 G 把磁盘吃满。两套语法完全不兼容——JDK 11 上用 -XX:+PrintGCDetails 会直接启动失败并提示改用 -Xlog。

四、容器内必配 ​

bash
-XX:+UseContainerSupport          # JDK 10+ 默认开,8u191+ 可用
-XX:MaxRAMPercentage=70.0
-XX:InitialRAMPercentage=70.0
-XX:+ExitOnOutOfMemoryError       # OOM 就退出,让编排系统重启

输出(验证是否识别到 limit):

text
$ java -XX:+PrintFlagsFinal -version | grep -E 'MaxHeapSize|MaxRAMPercentage'
   size_t MaxHeapSize = 3221225472  {pd product}   # 3G,对应 limit 4G × 70%

解释:老 JDK(8u131 之前)在容器里读的是宿主机内存,4G limit 的容器里起一个默认堆为宿主 1/4 的进程,直接被 cgroup 杀掉。MaxRAMPercentage 比写死 -Xmx 更适合弹性扩缩容的场景。

五、故障兜底参数(强烈建议默认带上) ​

bash
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dump/app.hprof
-XX:ErrorFile=/var/log/app/hs_err_pid%p.log
-XX:+ExitOnOutOfMemoryError

解释:OOM 发生的那一刻是唯一能看清"什么东西占着内存"的机会。不预先配 HeapDumpOnOutOfMemoryError,进程一旦被杀现场就没了,事后只能靠猜。%p 会替换成 pid,避免多实例互相覆盖。

HeapDumpPath 目录必须存在且可写

配成不存在的目录时 JVM 会打印 Unable to open heap dump file 然后什么都不落盘。上线前用 jcmd <pid> GC.heap_dump /data/dump/test.hprof 手动验证一次能不能真的写出来。

六、查当前进程实际生效的参数 ​

bash
jcmd <pid> VM.flags
jinfo -flag MaxHeapSize <pid>
jcmd <pid> VM.flags -all | grep -E 'GCTimeRatio|MaxGCPauseMillis'

输出:

text
-XX:CICompilerCount=4 -XX:ConcGCThreads=4 -XX:G1HeapRegionSize=8388608
-XX:MaxGCPauseMillis=200 -XX:MaxHeapSize=4294967296 -XX:MinHeapSize=4294967296

解释:jcmd 读的是运行时真值,比翻启动脚本可靠——脚本里可能被 JAVA_OPTS、CATALINA_OPTS、镜像 ENV 层层覆盖。jinfo -flag 看单项,jcmd VM.flags 看全量。

七、常见参数错误信号 ​

启动报错原因处理
Unrecognized VM option 'PrintGCDateStamps'JDK 9+ 用了旧版 GC 日志语法换 -Xlog:gc*
CMS is deprecated, this will be removed in a future releaseJDK 9+ 用 CMS换 G1
Could not reserve enough space for object heapXmx 大于可用内存/容器 limit调小或改百分比
Invalid maximum heap size on 32-bit JVM装了 32 位 JDK换 64 位
进程起来但 RSS 远超 Xmx堆外内存(直接内存/线程数/元空间)查 -XX:MaxDirectMemorySize 和线程数