这是《JVM 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 JVM 参数是运行时的控制面。理解参数体系,比背下一堆开关更重要:同一个目标在不同 JDK 版本、不同收集器、不同部署环境下,可能对应不同参数和默认值。
21.1 参数分类
常见 JVM 参数分为以下几类。
| 类型 | 示例 | 用途 |
|---|---|---|
| 标准参数 | -version、-jar |
大多数 JVM 都支持 |
-X 参数 |
-Xms、-Xmx、-Xss |
常用非标准参数 |
-XX 参数 |
-XX:+UseG1GC、-XX:MaxHeapFreeRatio |
JVM 实现级参数 |
| 系统属性 | -Dfile.encoding=UTF-8 |
传给 Java 应用 |
| 环境变量 | JAVA_TOOL_OPTIONS |
间接影响 JVM |
布尔参数写法:
-XX:+UseG1GC 开启
-XX:-UseG1GC 关闭
键值参数写法:
-XX:MaxHeapSize=4g
-XX:ActiveProcessorCount=4
21.2 内存参数
常用参数:
| 参数 | 含义 |
|---|---|
-Xms |
初始堆大小 |
-Xmx |
最大堆大小 |
-Xmn |
新生代大小 |
-XX:NewRatio |
老年代与新生代比例 |
-XX:SurvivorRatio |
Eden 与 Survivor 比例 |
-Xss |
单线程栈大小 |
-XX:MaxMetaspaceSize |
元空间上限 |
-XX:MaxDirectMemorySize |
直接内存上限 |
-XX:SoftMaxHeapSize |
ZGC 等收集器的软堆目标 |
典型配置:
java -Xms4g -Xmx4g \
-Xss1m \
-XX:MaxMetaspaceSize=512m \
-XX:MaxDirectMemorySize=1g \
-jar app.jar
生产服务通常建议 -Xms 等于 -Xmx,避免堆伸缩引入噪声。
21.3 收集器参数
常用收集器:
# Serial
java -XX:+UseSerialGC -jar app.jar
# Parallel
java -XX:+UseParallelGC -jar app.jar
# G1
java -XX:+UseG1GC -jar app.jar
# ZGC
java -XX:+UseZGC -jar app.jar
# Shenandoah,需运行时支持
java -XX:+UseShenandoahGC -jar app.jar
CMS 在新 JDK 中已被移除:
JDK 8:可用
JDK 9:标记 deprecated
JDK 14:移除
不要把旧博客中的 CMS 参数直接搬到 JDK 17 或 JDK 21。
21.4 OOM 与诊断参数
堆 dump 参数:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/dump
OOM 时主动退出,避免故障实例继续服务:
-XX:+ExitOnOutOfMemoryError
统一日志:
-Xlog:gc*,safepoint:file=/log/gc.log:time,uptime,level,tags:filecount=10,filesize=50m
JFR:
-XX:StartFlightRecording=filename=/log/start.jfr,maxsize=512m,maxage=1d
这些参数应在部署模板中统一配置,而不是等事故发生后手动添加。
21.5 容器相关参数
在容器中,关键是 JVM 能正确识别 CPU 和内存限制。
JDK 8u191+ 和 JDK 10+ 对容器支持明显增强。现代 LTS 通常默认会识别 cgroup 限制,但行为仍与 JVM 版本和容器运行时有关。
可用参数:
| 参数 | 用途 |
|---|---|
-XX:+UseContainerSupport |
启用容器感知 |
-XX:ActiveProcessorCount=4 |
显式指定可用 CPU 数 |
-XX:InitialRAMPercentage=50.0 |
初始堆占容器内存比例 |
-XX:MaxRAMPercentage=50.0 |
最大堆占容器内存比例 |
-XX:MinRAMPercentage=50.0 |
小容器场景比例 |
示例:
java -XX:+UseContainerSupport \
-XX:MaxRAMPercentage=60.0 \
-XX:ActiveProcessorCount=4 \
-jar app.jar
堆不能等于容器内存 limit,还要预留元空间、线程栈、直接内存、JIT 代码缓存和 Native 库。
21.6 查看参数
查看最终生效值:
java -XX:+PrintFlagsFinal -version
过滤指定参数:
java -XX:+PrintFlagsFinal -version | findstr /i "HeapSize GC"
Linux 下:
java -XX:+PrintFlagsFinal -version | grep -Ei 'HeapSize|Use.*GC'
查看运行中进程命令行:
jcmd <pid> VM.command_line
jcmd <pid> VM.flags
启动日志也是确认参数是否生效的可靠方式:
java -Xlog:jvm+init:file=jvm-init.log -jar app.jar
21.7 参数优先级
常见来源:
命令行
JAVA_TOOL_OPTIONS
_JAVA_OPTIONS
JDK_JAVA_OPTIONS
环境变量
不同来源的优先级与 JVM 版本、启动方式有关。生产排障时应以实际输出为准:
jcmd <pid> VM.command_line
容易踩坑的场景:
- 启动脚本、K8s YAML、镜像 ENTRYPOINT 各写一份参数;
- 基础镜像设置了
JAVA_TOOL_OPTIONS; - 平台注入了内存参数;
- 重复设置
-Xmx; - 参数拼写错误但启动仍成功。
21.8 参数治理
生产环境建议:
- 启动模板统一管理 JVM 参数;
- 所有实例版本一致或明确版本矩阵;
- dump 和日志目录提前创建并挂载;
- 把有效 JVM 参数作为实例元数据上报;
- 修改参数必须灰度并对比延迟与 GC;
- 移除无效参数,避免误导后续维护者;
- 避免把调优参数复制到不同负载的服务。
参数清单模板:
| 参数 | 当前值 | 目的 | 风险 | 验证方式 |
|---|---|---|---|---|
-Xmx |
4g | 控制堆上限 | 容器 OOM | jcmd VM.flags |
-XX:+UseG1GC |
true | 低延迟目标 | 吞吐下降 | GC 日志 |
-XX:MaxMetaspaceSize |
512m | 防元空间失控 | 加载失败 | 监控 |
21.9 常见错误
| 错误 | 后果 |
|---|---|
| 只设置堆,不考虑堆外 | 容器被 OOM Kill |
| 新生代设置过大 | 单次 Young GC 停顿变长 |
| 盲目调大线程栈 | 线程多时 Native 内存暴涨 |
| 使用已移除参数 | 启动失败或无效 |
| 未固定 dump 路径 | OOM 后拿不到证据 |
| 只看平均值 | 忽略长尾 |
| 复制他人参数 | 与业务负载不匹配 |
本章小结
JVM 参数体系要按内存、GC、诊断、容器和运行时行为分类理解。参数在不同 JDK 与部署环境中可能不同,生产上必须通过 PrintFlagsFinal、jcmd 和启动日志确认最终生效值。参数治理的重点是统一模板、可验证、可回滚,并把内存限制理解为整个进程而非只有堆。
思考题
-Xmx和容器 memory limit 有什么关系?- 如何确认一个运行中 JVM 的实际参数?
- 为什么要显式配置 OOM dump?
-XX:+UseConcMarkSweepGC在 JDK 17 上会发生什么?- 如何设计团队级 JVM 参数变更流程?