JVMNotes

第 21 章:JVM 参数体系

zjc 于 2026-01-21 发布

这是《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

容易踩坑的场景:

  1. 启动脚本、K8s YAML、镜像 ENTRYPOINT 各写一份参数;
  2. 基础镜像设置了 JAVA_TOOL_OPTIONS
  3. 平台注入了内存参数;
  4. 重复设置 -Xmx
  5. 参数拼写错误但启动仍成功。

21.8 参数治理

生产环境建议:

  1. 启动模板统一管理 JVM 参数;
  2. 所有实例版本一致或明确版本矩阵;
  3. dump 和日志目录提前创建并挂载;
  4. 把有效 JVM 参数作为实例元数据上报;
  5. 修改参数必须灰度并对比延迟与 GC;
  6. 移除无效参数,避免误导后续维护者;
  7. 避免把调优参数复制到不同负载的服务。

参数清单模板:

参数 当前值 目的 风险 验证方式
-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 与部署环境中可能不同,生产上必须通过 PrintFlagsFinaljcmd 和启动日志确认最终生效值。参数治理的重点是统一模板、可验证、可回滚,并把内存限制理解为整个进程而非只有堆。

思考题

  1. -Xmx 和容器 memory limit 有什么关系?
  2. 如何确认一个运行中 JVM 的实际参数?
  3. 为什么要显式配置 OOM dump?
  4. -XX:+UseConcMarkSweepGC 在 JDK 17 上会发生什么?
  5. 如何设计团队级 JVM 参数变更流程?