有哪些常用的 JVM 启动参数?
结论先行:JVM 参数按兼容性分三档——- 标准参数、-X 非标准参数、-XX 扩展参数;按用途只需记住四组:堆大小、收集器与 GC 日志、OOM 现场、诊断查询。调优的起点不是背参数表,而是先把「堆上限 + GC 日志 + OOM 转储」这三件事配齐,其余参数等观察到问题再加。
一、三档参数的区别
| 档位 | 形态 | 兼容性 | 典型例子 |
|---|---|---|---|
| 标准参数 | -version、-cp | 所有 JVM 实现都必须支持 | -classpath、-Dkey=value |
| 非标准参数 | -Xms、-Xmn | HotSpot 私有,跨版本不保证 | -Xss、-Xmx |
| 扩展参数 | -XX:+Flag、-XX:Flag=v | 最不稳定,随版本增删 | -XX:+UseG1GC |
-XX 有两种写法:布尔型用 + / - 开关(-XX:+PrintGCDetails),数值型用 = 赋值(-XX:MetaspaceSize=256m)。把布尔型写成 = 是最常见的失误,JVM 会拒绝启动并报 Unrecognized VM option,不会静默忽略。
二、堆与栈:先定容量
| 参数 | 含义 | 经验值 |
|---|---|---|
-Xms / -Xmx | 堆初始值 / 最大值 | 建议两者相等,避免运行期反复扩容 |
-Xmn | 新生代大小 | G1 下不建议显式设置 |
-Xss | 单线程栈大小 | Linux x64 默认 1M,深递归时才调大 |
-XX:MetaspaceSize | 元空间首次触发 GC 的阈值 | 只影响触发点,不是上限 |
-XX:MaxMetaspaceSize | 元空间上限 | 不设则只受本地内存限制 |
-XX:MaxDirectMemorySize | 堆外直接内存上限 | Netty / NIO 场景必须显式设置 |
三、收集器与 GC 日志
收集器参数只要记住「选一个 + 给一个停顿目标」:-XX:+UseG1GC -XX:MaxGCPauseMillis=200。日志参数在 JDK 9 之后被统一日志(unified logging)取代,新旧写法不能混用。
| 目的 | JDK 8 写法 | JDK 9+ 统一日志 |
|---|---|---|
| GC 明细 | -XX:+PrintGCDetails | -Xlog:gc* |
| 日期时间戳 | -XX:+PrintGCDateStamps | :time,uptime 输出装饰器 |
| 日志文件与滚动 | -XX:+UseGCLogFileRotation | file=gc.log:filesize=50m,filecount=5 |
| 存活年龄分布 | -XX:+PrintTenuringDistribution | -Xlog:gc+age=trace |
四、一份可直接抄的启动脚本
java -server \
-Xms4g -Xmx4g \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
-XX:MaxDirectMemorySize=1g \
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-Xlog:gc*,gc+heap=info:file=/var/log/app/gc-%p.log:time,uptime,level,tags:filesize=50m,filecount=5 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/oom.hprof \
-XX:ErrorFile=/var/log/app/hs_err_%p.log \
-Djava.security.egd=file:/dev/./urandom \
-jar app.jar
逐行看:-Xms 等于 -Xmx 是为了让堆一次要够;MaxMetaspaceSize 是给容器兜底;gc-%p.log 用进程号区分多实例;HeapDumpOnOutOfMemoryError 是唯一能在事后还原现场的开关,务必打开;urandom 解决容器内熵不足导致的启动卡顿。
五、OOM 与崩溃现场
| 参数 | 作用 |
|---|---|
-XX:+HeapDumpOnOutOfMemoryError | 堆 OOM 时导出 hprof |
-XX:HeapDumpPath=/path | 指定转储路径,容器内要挂载出来 |
-XX:OnOutOfMemoryError="/bin/kill -9 %p" | OOM 后执行脚本,常用于配合重启 |
-XX:+ExitOnOutOfMemoryError | OOM 直接退出,交给编排系统拉起 |
-XX:ErrorFile=/path/hs_err_%p.log | JVM 崩溃时的错误日志 |
注意 OnOutOfMemoryError 和 ExitOnOutOfMemoryError 会互相干扰,只留一个。
六、查询当前生效参数
jcmd 1234 VM.flags # 整个 JVM 的参数清单
jinfo -flag MaxHeapSize 1234 # 单个参数的最终取值
jinfo -flags 1234 # 只看被显式设置的参数
java -XX:+PrintFlagsFinal -version | grep -i heapsize
PrintFlagsFinal 只列 -XX 参数,-Xms 实际上是 -XX:InitialHeapSize 的别名,-Xmx 是 -XX:MaxHeapSize,所以查不到 -Xmx 不代表没生效。
七、常见追问
| 追问 | 回答要点 |
|---|---|
为什么 -Xms 要等于 -Xmx | 堆扩缩本身带来停顿和抖动;容器里用 -XX:MaxRAMPercentage=75 自适应更稳妥 |
-Xmn 和 -XX:NewRatio 冲突吗 | 同时给时 -Xmn 优先;NewRatio 表示老年代/新生代容量比 |
| 元空间会 OOM 吗 | 会,Metaspace 或 Compressed class space 溢出;不设上限则吃光容器内存被 OOMKilled |
-XX:+DisableExplicitGC 能用吗 | 能屏蔽业务代码里的 System.gc(),但也会挡掉 DirectByteBuffer 依赖的清理触发,慎用 |
| 参数写错会静默忽略吗 | 不会,启动即失败;-XX:+IgnoreUnrecognizedVMOptions 只在跨版本复用脚本时短期使用 |
小结
参数分三档,实战只需盯住「容量、收集器与日志、现场保留」四条线。先开日志和 dump,再谈调优;任何一个参数上线前,用 jcmd VM.flags 确认它真的生效了。