一、常用命令

jps -l 查看 Java 进程的 PID

jinfo -flags <pid> 查看 JVM 启动时的具体参数

jcmd <pid> VM.flags 查看 JVM 启动时的具体参数(比 jinfo 更常用)

jcmd <PID> GC.class_histogram -all | tail -n 10 查看存活对象统计(-all 参数表示只统计当前存活的对象),输出结果的尾行第一个数字是当前堆中存活的对象总个数,第二个数字是当前实际存活对象的总大小,单位是字节(Byte)。

jstat -gc <PID> | awk '{print "老年代实际存活: " $8/1024 " MB"}'

jcmd <PID> GC.run 手动触发一次 Full GC

docker stast 用于实时查看 Docker 容器的资源使用情况,查看具体服务使用 docker stats <CONTAINER_ID>

jcmd <pid> GC.heap_info 查看堆配置和当前堆使用情况

jcmd <pid> VM.native_memory summary 查看 JVM 总内存构成,排查 OOMKilled、内存泄漏等问题

jmap -histo:live <pid> 专门用来查看当前 JVM 堆内存中所有存活对象的直方图(Histogram)

jstat -gc <PID> | awk '{print ($3+$4+$6+$8)/1024 " MB"}' 大概的看一眼堆内存使用大小

堆外内存估算方法:

以下是生产环境中最核心的启动参数与 jcmd VM.flags 输出的精确一一对应表格,以及它们在输出中的实际长相:

常用启动参数 jcmd 对应的 -XX: 变量名 单位与含义说明
-Xms InitialHeapSize 初始堆大小(字节)。例如 InitialHeapSize=2147483648 (2G)
-Xmx MaxHeapSize 最大堆大小(字节)。例如 MaxHeapSize=4294967296 (4G)
-Xmn NewSize / MaxNewSize 新生代大小。-Xmn 会同时将这两个变量设为相同的值
-Xss ThreadStackSize 每个线程的栈大小(注意:单位通常是 KB)。例如 ThreadStackSize=1024 代表 1MB
-XX:MetaspaceSize MetaspaceSize 元空间触发 Full GC 的初始阈值(字节)
-XX:MaxMetaspaceSize MaxMetaspaceSize 元空间允许的最大大小(字节)
-XX:MaxDirectMemorySize MaxDirectMemorySize 最大堆外直接内存大小(字节,默认为 0 表示由 JVM 自动决定)
-XX:SurvivorRatio SurvivorRatio Eden 区与单个 Survivor 区的比例(默认 8,即占 $\frac{8}{10}$)
-XX:+UseG1GC UseG1GC=true 代表当前应用正在使用 G1 垃圾回收器
-XX:+HeapDumpOnOutOfMemoryError HeapDumpOnOutOfMemoryError=true 确认 OOM 时自动导出堆快照功能已开启
-XX:HeapDumpPath HeapDumpPath 后面会直接接字符串路径,如 HeapDumpPath=/logs

jcmd <pid> VM.flags 默认只打印被修改过的、非默认的参数。如果你在输出里找不到 ThreadStackSizeMaxDirectMemorySize,说明你在启动时根本没有配它,它目前正在使用 JVM 的默认值。如果你想连同默认值一起看,彻底对齐所有参数,请使用这个进阶命令:jcmd <pid> VM.flags -all。它会把 700 多个内部变量全部列出。你可以通过 | grep InitialHeapSize 或者是 MaxRAMPercentage 来精准狙击你想核对的任何隐藏指标。


查看堆外内存使用率,最有用的是开使用 Native Memory Tracking(NMT)

  1. 启动参数:-XX:NativeMemoryTracking=summary,或者更详细:-XX:NativeMemoryTracking=detail
  2. 检查是否开启:jcmd <pid> VM.native_memory
  3. 查看汇总:jcmd <pid> VM.native_memory summary
  4. 查看输出结果

差值对比诊断(最强排查技巧)

  1. 刚启动或运行正常时,设定基线:jcmd 1 VM.native_memory baseline
  2. 运行一段时间后或内存开始暴涨时,查看增长差值:jcmd 1 VM.native_memory summary

ParalleGC 参数建议配置:

1. 显式指定使用 Parallel GC
-XX:+UseParallelGC

# 2. 开启容器自适应支持(JDK 8u191+ / 11 / 17 默认开启,显式写出更稳妥)
-XX:+UseContainerSupport

# 3. 严格限制堆内存比例(核心防线)
-XX:MaxRAMPercentage=70.0
-XX:InitialRAMPercentage=70.0

# 4. 显式限制堆外内存大小
-XX:MaxDirectMemorySize=512m

# 5. 优化元空间与线程栈
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=256m
-Xss256k

根据 JVM 性能调优圣经,为了让 Parallel GC 达到吞吐量最大化,各代大小应满足:

  • 老年代(Old Gen)配置 = 活跃数据的 2 ~ 3 倍
  • 年轻代(Young Gen)配置 = 活跃数据的 1 ~ 1.5 倍
  • 总堆内存(Xmx / Xms) = 活跃数据的 3 ~ 4.5 倍

活跃数据可以手动进行一次 GC 之后查看:jcmd <pid> VM.run 或者 jmap -histo:live <pid>

举个例子:通过 jstat 或 GC 日志发现,Full GC 后老年代还剩 600M 数据(这就是活跃数据)。

  • 老年代分配:$600\text{M} \times 2 = 1200\text{M}$
  • 年轻代分配:$600\text{M} \times 1.5 = 900\text{M}$
  • 最终堆大小:$1200\text{M} + 900\text{M} = 2100\text{M}$(约 2G)。如果容器是 4G,这证明 2G~2.5G 的堆对 Parallel 来说极其宽裕。

对象晋升过程:

```Eden
↓ GC
S0(age=1)

↓ GC
S1(age=2)

↓ GC
S0(age=3)

↓ GC
S1(age=4)

达到阈值:-XX:MaxTenuringThreshold=15,或者 Survivor 放不下,晋升到 Old。