虽说日常开发很少接触到 JVM 的命令行参数吧,但了解一些常用的,明白它们分别是做什么的,对于问题排查和性能调优也是有帮助的。
启动参数
java 命令后的参数分三类:
- 标准参数(
-):如-classpath、-version,所有 JVM 实现都支持; - 非标准参数(
-X):HotSpot 特有,不保证跨实现可用; - 不稳定参数(
-XX):官方声明随时可能变更,却是调优的主力。-XX:+Flag/-XX:-Flag是布尔开关,-XX:Flag=value带值。
$$ java -Xms512m -Xmx1024m -Xss256k -jar app.jar
-Xms/-Xmx:初始 / 最大堆大小,两者设为一致可避免堆反复伸缩;-Xss:每个线程的栈大小,线程多而栈不深的场景调小它能容纳更多线程。
打开 GC 日志(JDK 9+ 统一日志语法;JDK 8 用 -XX:+PrintGCDetails -Xloggc:gc.log):
$$ java -Xmx1g -Xlog:gc*:file=gc.log:time,uptime,level,tags -jar app.jar
OOM 时自动留现场的保险丝:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/with/enough/space
命令行工具
均在 JDK bin 目录下。套路是固定的:jps 找到 pid,再按需用下面的工具。
jps:查看 Java 进程
类似 ps,只列出 JVM 进程。-q 只输出 pid,-l 输出主类全名(jar 则为路径),-v 输出 JVM 参数,-m 输出传给 main() 的参数。
$$ jps -l
12345 com.example.App
67890 sun.tools.jps.Jps
jstat:监视 GC 等运行数据
格式为 jstat -<option> [-t] [-h<n>] <vmid> [<间隔> [<次数>]]。例如 jstat -gc -h3 12345 1000 10:每 1s 打印一次共 10 次,每 3 行重复表头;加 -t 在输出前加时间戳列。
$$ jstat -gcutil 12345 1000
S0 S1 E O M YGC YGCT FGC FGCT GCT
0.00 98.44 45.12 67.89 95.23 42 0.836 3 1.102 1.938
YGC涨太快:分配速率过高或新生代太小;FGC持续增长且FGCT很大:老年代回收赶不上分配,重点怀疑泄漏。
其他常用 option:-class(类加载)、-compiler(JIT)、-gc(各区容量与用量)、-gcnew / -gcold(新生代、老年代行为)。连续采样比单点数据有用。
jinfo:查看和动态修改参数
$$ jinfo -flag MaxHeapSize 12345 # 查单个 flag
$$ jinfo -sysprops 12345 # 系统属性
$$ jinfo -flag +HeapDumpOnOutOfMemoryError 12345 # 不重启动态打开
在线上动态开关 flag 是它最有价值的场景:忘配了 HeapDump 参数,等不及重启就用它补上。只有标记为 manageable 的参数支持运行时修改,并非全部。
jmap:生成堆转储快照
$$ jmap -histo:live 12345 | head -20
$$ jmap -dump:live,format=b,file=heap.hprof 12345
内存告警先看 -histo:live 直方图,某个业务类占用异常高基本当场定位;需要深挖再导出 hprof,用 MAT 或 VisualVM 离线分析,重点看支配树和到 GC Roots 的引用链。
注意 -dump:live 会先触发一次 Full GC,对大堆是可观的停顿,生产慎用;也可以不手动 dump,靠 -XX:+HeapDumpOnOutOfMemoryError 自动留档。
jstack:生成线程快照
定位线程长时间停顿的原因:死锁、死循环、等锁、等外部资源。每隔几秒连续抓两份以上对比——多份栈停在同一处才可信。
$$ jstack 12345 > thread1.txt
- CPU 飙高:
top -H -p <pid>找到热点线程,printf '%x\n' <tid>把十进制线程 ID 转为十六进制,到栈里搜nid=0x...; - 死锁:输出末尾会直接列出
Found one Java-level deadlock和互相等待的线程。
jcmd:一统江湖
JDK 7+ 的综合工具,覆盖 jinfo / jmap / jstack 的大部分功能:
$$ jcmd 12345 VM.flags # jinfo -flags
$$ jcmd 12345 GC.heap_info # 堆各区概况
$$ jcmd 12345 Thread.print -l # jstack
$$ jcmd 12345 GC.class_histogram # jmap -histo
$$ jcmd 12345 GC.heap_dump heap.hprof # jmap -dump
$$ jcmd 12345 help # 列出全部子命令
还能录制 JFR(JDK 11 起免费开源,开销极低,适合线上):
$$ jcmd 12345 JFR.start duration=60s filename=rec.jfr settings=profile
排查套路
jps 找 pid → jstat -gcutil <pid> 1000 10 看 GC 趋势 → CPU 高:top -H + jstack,内存高:jmap -histo:live → 深挖再 jcmd 拉 dump / 录 JFR → 事后补 -XX:+HeapDumpOnOutOfMemoryError。