jstat
JVM监控是Java应用运维的核心环节,通过jstat、jmap系列命令可实现GC实时监控、内存配置核查、对象占用分析及堆快照dump。本文针对宿主机与Docker容器两种场景,详细拆解各命令用法、输出解读、容器化适配技巧及注意事项,重点强化Docker环境下的权限、进程隔离、日志持久化等关键要点,适用于JDK8及以上版本。
一、实时监控JVM GC情况(jstat -gcutil)
jstat -gcutil命令用于轻量级实时采集JVM各内存区域使用率、GC次数及耗时,是排查GC频繁、耗时过长的首选工具,宿主机与Docker场景用法存在差异,需针对性适配。
- 基础语法(通用)
jstat -gcutil <pid> <interval> <count>
- pid:Java进程ID(宿主机为系统进程ID,Docker容器内为容器内进程ID,通常为1);
- interval:监控间隔,单位毫秒(如1000表示每秒采集1次);
- count:采集次数(省略则持续采集,需手动Ctrl+C终止)。
- 宿主机场景实操
# 1. 获取Java进程ID(以Tomcat为例)
ps -ef | grep java | grep -v grep # 过滤出目标进程ID,如12345
# 2. 执行监控命令,每秒采集1次,共100次
jstat -gcutil 12345 1000 100
# 3. 日志重定向(便于后续分析)
jstat -gcutil 12345 1000 100 > /var/log/jvm/gc.log 2>&1
- Docker容器场景实操(核心重点)
Docker容器内进程与宿主机隔离,无法直接通过宿主机进程ID监控,需进入容器或通过docker exec命令执行,步骤如下:
# 场景1:进入容器内执行(交互模式)
# 1. 查看容器列表,获取目标容器名/ID(以tomcat容器为例)
docker ps | grep tomcat # 假设容器名为tomcat-app,ID为abc123
# 2. 进入容器(--user root避免权限不足)
docker exec -it --user root tomcat-app /bin/bash
# 3. 容器内获取Java进程ID(单一应用容器通常为1)
ps -ef | grep java | grep -v grep # 输出进程ID为1
# 4. 执行监控命令(容器内JDK路径通常为/opt/java/openjdk/bin/,需确认路径)
/opt/java/openjdk/bin/jstat -gcutil 1 1000 100
# 场景2:宿主机直接执行(非交互模式,推荐运维自动化)
# 直接调用容器内jstat,日志输出到宿主机
docker exec --user root tomcat-app /opt/java/openjdk/bin/jstat -gcutil 1 1000 100 > /var/log/jvm/gc-container.log 2>&1
# 场景3:后台持续监控(避免终端阻塞)
nohup docker exec --user root tomcat-app /opt/java/openjdk/bin/jstat -gcutil 1 1000 > /var/log/jvm/gc-container.log 2>&1 &
- 输出结果解读
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 5.23 68.91 42.15 91.32 89.45 236 3.782 12 2.156 5.938
| 字段 | 含义 | 关键监控阈值 |
|---|---|---|
| S0/S1 | Survivor 0/1区使用率(%) | 交替空闲,无固定阈值,需结合GC频率判断 |
| E | Eden区使用率(%) | 超过85%频繁触发Minor GC,需检查新生代大小 |
| O | 老年代使用率(%) | 超过75%易触发Full GC,超过90%存在内存风险 |
| M | 元空间使用率(%) | 超过90%需检查类加载是否异常(如类泄漏) |
| YGC/FGC | Minor GC/Full GC总次数 | Full GC每小时超过2次需排查原因 |
| YGCT/FGCT | Minor GC/Full GC总耗时(秒) | Full GC单次耗时>1秒影响业务响应 |
- 注意事项
- Docker场景需确认容器内JDK路径,不同镜像路径可能不同(如官方Tomcat镜像为/opt/java/openjdk/bin/,自定义镜像可能为/usr/local/jdk/bin/);
- 容器内执行命令需保证root权限,否则可能无法读取进程信息;
- 监控间隔不宜过短(≥500ms),避免增加容器内CPU负担。
二、查看JVM堆内存配置信息(jmap -heap)
jmap -heap命令用于查看Java进程的堆内存整体配置、各区域大小及使用情况,可快速验证JVM启动参数(-Xms、-Xmx等)是否生效,适配宿主机与Docker场景。
- 基础语法(通用)
jmap -heap <pid>
- 宿主机场景实操
# 1. 获取Java进程ID
ps -ef | grep java | grep -v grep # 如12345
# 2. 执行命令查看配置
jmap -heap 12345
# 输出包含堆配置(-Xms/-Xmx/-Xmn等)、各区域使用情况、GC回收器类型
- Docker容器场景实操
# 方式1:进入容器内执行
docker exec -it --user root tomcat-app /bin/bash
/opt/java/openjdk/bin/jmap -heap 1 # 容器内Java进程ID为1
# 方式2:宿主机直接执行,输出到文件便于查看
docker exec --user root tomcat-app /opt/java/openjdk/bin/jmap -heap 1 > /var/log/jvm/jvm-heap-config.log 2>&1
cat /var/log/jvm/jvm-heap-config.log
- 核心输出解读(关键部分)
Heap Configuration: # JVM堆配置(对应启动参数)
MaxHeapSize = 2147483648 (2048.0MB) # -Xmx配置值
NewSize = 536870912 (512.0MB) # -Xmn配置值
SurvivorRatio = 8 # -XX:SurvivorRatio配置值
MetaspaceSize = 67108864 (64.0MB) # -XX:MetaspaceSize配置值
Heap Usage: # 实际使用情况
New Generation (Eden + 1 Survivor Space):
capacity = 482344960 (460.0MB)
used = 235929600 (225.0MB)
48.91% used
tenured generation: # 老年代
capacity = 1610612736 (1536.0MB)
used = 655360000 (625.0MB)
40.69% used
- 注意事项
- 该命令会触发短暂STW(停止世界),Docker容器内执行需避开业务峰值,避免影响容器内应用响应;
- 若容器内提示“Permission denied”,需添加--user root参数进入容器,确保权限充足;
- 可通过输出对比配置值与实际使用情况,排查JVM参数配置错误(如参数未生效、配置不合理)。
三、查看JVM类及对象占用情况(jmap -histo)
jmap -histo命令用于统计堆内存中各类的实例数量、占用内存大小,可快速定位大对象、频繁创建的对象,为排查内存泄漏、优化对象创建逻辑提供依据,支持存活对象过滤。
- 基础语法(通用)
# 统计所有对象
jmap -histo <pid> | head -n <行数> # head筛选前N行,避免输出过多
# 仅统计存活对象(触发Full GC,谨慎使用)
jmap -histo:live <pid> | head -n <行数>
- 宿主机场景实操(示例:查看前20类)
ps -ef | grep java | grep -v grep # 进程ID 12345
jmap -histo 12345 | head -20
- Docker容器场景实操
# 方式1:进入容器内查看前20类
docker exec -it --user root tomcat-app /bin/bash
/opt/java/openjdk/bin/jmap -histo 1 | head -20
# 方式2:宿主机导出结果到文件
docker exec --user root tomcat-app /opt/java/openjdk/bin/jmap -histo 1 | head -50 > /var/log/jvm/jvm-object-stat.log 2>&1
# 方式3:统计容器内存活对象(触发Full GC,线上避峰)
docker exec --user root tomcat-app /opt/java/openjdk/bin/jmap -histo:live 1 | head -50 > /var/log/jvm/jvm-live-object-stat.log 2>&1
- 输出结果解读
num #instances #bytes class name
----------------------------------------------
1: 9876 13543680 com.example.Order # 自定义业务类
2: 15632 9214560 java.lang.String # 字符串对象
3: 6789 7654320 java.util.HashMap # 集合对象
4: 4321 5432160 [C # char数组(字符串底层)
- #instances:实例数量,异常过多(如百万级)需排查对象是否未释放;
- #bytes:占用内存(字节),换算为MB(除以1024*1024),大对象需重点分析;
- class name:类全名,[C为char数组、[B为byte数组,自定义类占用过高需排查业务逻辑。
- 注意事项
- 添加:live参数会触发Full GC,回收无用对象,确保统计准确性,但会产生STW,Docker容器内执行需严格避峰;
- 容器内对象统计结果仅反映容器内堆状态,与宿主机无关,需结合业务场景判断是否存在内存泄漏;
- 建议多次执行对比结果,若某类对象数量持续增长,大概率存在内存泄漏。
四、堆内存dump及离线分析(jmap -dump)
jmap -dump命令用于生成堆内存完整快照(包含所有对象及引用关系),结合Jvisualvm、MAT工具可精准定位内存泄漏、大对象引用链等复杂问题,Docker场景需解决快照文件导出问题。
- 基础语法(通用)
# 生成完整堆快照
jmap -dump:format=b,file=<文件名>.hprof <pid>
# 生成存活对象堆快照(减少文件体积)
jmap -dump:live,format=b,file=<文件名>.hprof <pid>
- format=b:二进制格式,工具仅支持此格式;
- file:快照文件路径及名称,后缀建议为.hprof。
- 宿主机场景实操
ps -ef | grep java | grep -v grep # 进程ID 12345
jmap -dump:format=b,file=/var/log/jvm/jvm-heap-dump.hprof 12345
# 用Jvisualvm分析(本地启动)
jvisualvm # 装入生成的.hprof文件,分析引用链及泄漏点
- Docker容器场景实操(核心:快照导出宿主机)
Docker容器内生成的快照文件需导出到宿主机,否则容器删除后文件丢失,两种实现方式如下:
# 方式1:先在容器内生成快照,再复制到宿主机
# 1. 进入容器生成快照
docker exec -it --user root tomcat-app /bin/bash
/opt/java/openjdk/bin/jmap -dump:format=b,file=/tmp/jvm-heap-dump.hprof 1
# 2. 宿主机执行复制命令,将容器内文件导出
docker cp tomcat-app:/tmp/jvm-heap-dump.hprof /var/log/jvm/
# 3. (可选)删除容器内临时文件,释放空间
docker exec --user root tomcat-app rm -rf /tmp/jvm-heap-dump.hprof
# 方式2:通过数据卷挂载,直接生成到宿主机(推荐长期使用)
# 1. 启动容器时挂载宿主机目录到容器内(如/var/log/jvm:/tmp/jvm)
docker run -d -v /var/log/jvm:/tmp/jvm --name tomcat-app tomcat:8.5
# 2. 直接生成快照到挂载目录(自动同步到宿主机)
docker exec --user root tomcat-app /opt/java/openjdk/bin/jmap -dump:format=b,file=/tmp/jvm/jvm-heap-dump.hprof 1
- 离线分析工具使用
(1)Jvisualvm(简单易用,适合快速排查)
- 启动Jvisualvm(JDK自带,命令行输入jvisualvm);
- 点击「文件」→「装入」,选择导出到宿主机的.hprof文件;
- 通过「堆Dump」标签查看对象分布,「内存泄漏检测」功能自动识别泄漏可疑点。
(2)MAT(功能强大,适合复杂内存泄漏)
-
下载安装MAT(官网:https://www.eclipse.org/mat/);
-
打开.hprof文件,自动生成分析报告;
-
通过「Leak Suspects」定位泄漏根源,「Dominator Tree」查看大对象引用链。
-
注意事项
- 堆快照文件体积接近堆内存大小(如2G堆生成2G左右文件),需预留足够磁盘空间,Docker场景避免占用容器内存储空间;
- 生成快照会触发STW,耗时与堆大小相关(2G堆约5-15秒),线上需在备机复现或业务低峰期执行;
- Docker数据卷挂载需注意权限,宿主机目录需赋予777权限(chmod 777 /var/log/jvm),避免容器内无法写入。
五、Docker注意事项
- 进程隔离原则:容器内进程ID与宿主机完全独立,宿主机工具无法直接访问容器内进程,所有命令需通过docker exec执行或进入容器操作;
- JDK路径一致性:容器内JDK路径需提前确认,不同镜像路径差异较大,可通过docker exec容器执行「which java」查询;
- 权限管控:默认用户可能无权限访问进程信息,执行命令需添加--user root参数,确保root权限;
- 日志/快照持久化:容器内临时文件易丢失,建议通过数据卷挂载宿主机目录,将监控日志、堆快照直接生成到宿主机;
- 性能影响控制:触发STW的命令(jmap -heap、jmap -histo:live、jmap -dump)需严格避峰,容器化环境资源隔离,STW过长可能导致容器被Docker杀死;
- 工具缺失处理:部分精简镜像可能缺少jstat、jmap工具,可在Dockerfile中基于原镜像安装完整JDK,或从宿主机复制工具包到容器内。

浙公网安备 33010602011771号