j-1 jstat -gcutil 命令详解
一、jstat -gcutil 命令详解
1.1 命令格式和参数
jstat -gcutil <pid> <interval> <count>
# 示例:jstat -gcutil 12345 1000 10
# 每1秒采样一次,共采样10次
1.2 输出结果示例
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 99.73 79.06 42.63 95.58 91.67 1441 23.232 12 1.234 24.466
0.00 99.73 79.06 42.63 95.58 91.67 1441 23.232 12 1.234 24.466
1.3 各列详细解释
| 列名 | 全称 | 说明 | 正常范围 | 异常判断 |
|---|---|---|---|---|
| S0 | Survivor 0 | 第一个Survivor区使用百分比 | 0-100% | 持续100%可能对象晋升慢 |
| S1 | Survivor 1 | 第二个Survivor区使用百分比 | 0-100% | 持续100%可能对象晋升慢 |
| E | Eden | Eden区使用百分比 | 0-100% | 增长过快可能创建大量临时对象 |
| O | Old | 老年代使用百分比 | <80% | >80%可能触发Full GC |
| M | Metaspace | 元空间使用百分比 | <90% | >90%可能Metaspace OOM |
| CCS | Compressed Class Space | 压缩类空间使用百分比 | <90% | >90%需关注 |
| YGC | Young GC Count | 年轻代GC次数 | - | 增长过快可能内存泄漏 |
| YGCT | Young GC Time | 年轻代GC总时间(秒) | - | YGCT/YGC > 0.1秒需关注 |
| FGC | Full GC Count | Full GC次数 | 越少越好 | >1次/小时需关注 |
| FGCT | Full GC Time | Full GC总时间(秒) | 越少越好 | FGCT/FGC > 1秒需优化 |
| GCT | GC Total Time | GC总时间(秒) | - | GCT/运行时间 > 5%需优化 |
1.4 实战分析案例
案例1:Eden区增长过快
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.0 100.0 95.3 45.2 78.4 82.1 152 3.456 5 0.987 4.443
0.0 100.0 98.7 45.2 78.4 82.1 153 3.478 5 0.987 4.465
0.0 0.0 12.3 45.8 78.4 82.1 154 3.512 5 0.987 4.499 # YGC+1,Eden清空
分析:Eden区从95.3%快速到98.7%,触发YGC,对象晋升到Old区(O从45.2%到45.8%)
案例2:频繁Full GC
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.0 100.0 34.2 89.7 62.1 58.9 245 8.912 23 15.678 24.590
0.0 0.0 12.5 89.8 62.1 58.9 246 8.945 23 15.678 24.623
0.0 100.0 56.7 89.9 62.1 58.9 247 8.978 24 16.123 25.101 # FGC+1
分析:老年代使用率89.7%接近Full GC阈值,频繁Full GC(FGCT高达15.678秒)
案例3:Survivor区异常
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
100.0 0.0 45.6 23.4 34.5 29.8 123 2.345 0 0.000 2.345
100.0 0.0 67.8 23.4 34.5 29.8 123 2.345 0 0.000 2.345
100.0 0.0 89.2 23.4 34.5 29.8 123 2.345 0 0.000 2.345
分析:S0持续100%,S1持续0%,说明对象在Survivor区之间没有正常交换
二、jstat -gc 命令详解
2.1 命令格式和参数
jstat -gc <pid> <interval> <count>
# 示例:jstat -gc 12345 1000 5
# 每1秒采样一次,共采样5次
2.2 输出结果示例(G1 GC)
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
0.0 1024.0 0.0 1024.0 8192.0 8192.0 10240.0 5120.0 105676.0 102400.0 14080.0 12800.0 1441 23.232 12 1.234 24.466
2.3 各列详细解释(容量单位为KB)
年轻代相关
| 列名 | 全称 | 说明 | 计算公式 | 监控要点 |
|---|---|---|---|---|
| S0C | Survivor 0 Capacity | S0区总容量(KB) | - | 与-Xmn比例相关 |
| S1C | Survivor 1 Capacity | S1区总容量(KB) | - | 通常等于S0C |
| S0U | Survivor 0 Used | S0区已使用(KB) | - | S0U/S0C = gcutil的S0 |
| S1U | Survivor 1 Used | S1区已使用(KB) | - | S1U/S1C = gcutil的S1 |
| EC | Eden Capacity | Eden区总容量(KB) | - | 年轻代主要部分 |
| EU | Eden Used | Eden区已使用(KB) | - | EU/EC = gcutil的E |
老年代相关
| 列名 | 全称 | 说明 | 计算公式 | 监控要点 |
|---|---|---|---|---|
| OC | Old Capacity | 老年代总容量(KB) | - | 堆大小 - 年轻代大小 |
| OU | Old Used | 老年代已使用(KB) | - | OU/OC = gcutil的O |
元空间相关
| 列名 | 全称 | 说明 | 计算公式 | 监控要点 |
|---|---|---|---|---|
| MC | Metaspace Capacity | 元空间总容量(KB) | - | 受-XX:MaxMetaspaceSize限制 |
| MU | Metaspace Used | 元空间已使用(KB) | - | MU/MC = gcutil的M |
| CCSC | Compressed Class Space Capacity | 压缩类空间容量(KB) | - | -XX:CompressedClassSpaceSize |
| CCSU | Compressed Class Space Used | 压缩类空间已使用(KB) | - | CCSU/CCSC = gcutil的CCS |
GC统计
| 列名 | 全称 | 说明 | 单位 |
|---|---|---|---|
| YGC | Young GC Count | 年轻代GC次数 | 次 |
| YGCT | Young GC Time | 年轻代GC总时间 | 秒 |
| FGC | Full GC Count | Full GC次数 | 次 |
| FGCT | Full GC Time | Full GC总时间 | 秒 |
| GCT | GC Total Time | GC总时间 | 秒 |
2.4 不同GC收集器的输出差异
Parallel GC(默认)
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
5120.0 5120.0 0.0 5120.0 32768.0 32768.0 65536.0 32768.0 32000.0 29999.0 4000.0 3800.0 100 5.123 5 2.456 7.579
G1 GC
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
0.0 1024.0 0.0 1024.0 8192.0 8192.0 10240.0 5120.0 105676.0 102400.0 14080.0 12800.0 1441 23.232 12 1.234 24.466
注意:G1的EC和OC是动态变化的,S0C/S1C可能为0
CMS GC
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
2048.0 2048.0 0.0 2048.0 16384.0 16384.0 40960.0 20480.0 32000.0 29999.0 4000.0 3800.0 100 5.123 2 0.500 5.623
2.5 实战计算示例
# 示例数据:
# S0C=2048, S0U=0, S1C=2048, S1U=2048, EC=8192, EU=4096, OC=20480, OU=10240
# MC=32768, MU=16384, CCSC=4096, CCSU=2048, YGC=100, YGCT=5.0, FGC=2, FGCT=1.0
# 计算使用率
S0使用率 = S0U / S0C = 0 / 2048 = 0%
S1使用率 = S1U / S1C = 2048 / 2048 = 100%
Eden使用率 = EU / EC = 4096 / 8192 = 50%
Old使用率 = OU / OC = 10240 / 20480 = 50%
Metaspace使用率 = MU / MC = 16384 / 32768 = 50%
CCS使用率 = CCSU / CCSC = 2048 / 4096 = 50%
# 计算平均GC时间
平均YGC时间 = YGCT / YGC = 5.0 / 100 = 0.05秒
平均FGC时间 = FGCT / FGC = 1.0 / 2 = 0.5秒
GC总时间占比 = GCT / 进程运行时间
# 计算堆内存总量
总堆内存 = EC + S0C + S1C + OC = 8192 + 2048 + 2048 + 20480 = 32768KB = 32MB
已使用堆内存 = EU + S0U + S1U + OU = 4096 + 0 + 2048 + 10240 = 16384KB = 16MB
堆使用率 = 已使用堆内存 / 总堆内存 = 16MB / 32MB = 50%
三、常见问题诊断模式
3.1 内存泄漏诊断
模式识别:
# 连续采样观察
jstat -gcutil 12345 1000 10
# 输出示例(内存泄漏):
时间点1: O=45%, YGC=100, FGC=2
时间点2: O=55%, YGC=120, FGC=3 # Old区增长,YGC增加
时间点3: O=65%, YGC=140, FGC=5 # 持续增长
时间点4: O=75%, YGC=160, FGC=8 # FGC频率增加
时间点5: O=85%, YGC=180, FGC=12 # 接近OOM
分析:
- Old区使用率持续上升
- YGC次数增加但无法回收Old区对象
- FGC频率增加,但回收效果差
3.2 年轻代配置不合理
模式识别:
# 年轻代过小
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.0 100.0 95.0 30.0 45.0 40.0 5000 250.0 50 25.0 275.0
# YGC次数极高(5000次),说明对象很快填满Eden
# 年轻代过大
0.0 100.0 10.0 80.0 45.0 40.0 50 30.0 10 10.0 40.0
# YGC次数少但每次时间长(平均0.6秒),老年代使用率高
3.3 Full GC频繁
诊断步骤:
# 1. 监控FGC增长
watch -n 1 'jstat -gcutil 12345 | awk "{print \$9, \$10, \$11}"'
# 2. 分析Full GC触发原因
# 原因1:Old区空间不足
# 原因2:System.gc()调用
# 原因3:Metaspace不足
# 原因4:Promotion Failed
# 3. 检查GC日志确认
tail -f gc.log | grep "Full GC"
3.4 元空间泄漏
模式识别:
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.0 100.0 45.6 23.4 95.8 92.1 123 2.345 0 0.000 2.345
0.0 100.0 56.7 23.4 96.2 92.5 124 2.378 0 0.000 2.378
0.0 100.0 67.8 23.4 96.8 93.0 125 2.412 0 0.000 2.412
分析: Metaspace(M)持续增长接近100%,可能动态生成类过多
四、自动化监控脚本
4.1 实时监控脚本
#!/bin/bash
# monitor_jstat.sh
PID=$1
INTERVAL=${2:-1000}
COUNT=${3:-10}
echo "开始监控进程 $PID,间隔 ${INTERVAL}ms,次数 $COUNT"
echo "时间 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT"
echo "--------------------------------------------------------------------------------"
jstat -gcutil $PID $INTERVAL $COUNT | awk -v interval=$INTERVAL '
NR>1 {
# 计算时间戳
timestamp = strftime("%Y-%m-%d %H:%M:%S");
# 判断异常并标记颜色
color = "";
reset = "";
if ($3 > 90) color = "\033[33m"; # Eden > 90% 黄色警告
if ($4 > 80) color = "\033[31m"; # Old > 80% 红色警告
if ($5 > 90) color = "\033[35m"; # Metaspace > 90% 紫色警告
if ($9 > 0) color = "\033[41m\033[37m"; # 发生FGC 红底白字
reset = "\033[0m";
printf "%s %s%6.1f %6.1f %6.1f %6.1f %6.1f %6.1f %5d %7.3f %4d %6.3f %7.3f%s\n",
timestamp, color, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, reset;
}'
4.2 历史数据分析脚本
#!/bin/bash
# analyze_jstat_history.sh
LOG_FILE=$1
TARGET_PID=$2
echo "=== JStat历史数据分析报告 ==="
echo "分析文件: $LOG_FILE"
echo "目标PID: $TARGET_PID"
echo "生成时间: $(date)"
echo ""
# 提取并分析数据
awk -v pid="$TARGET_PID" '
BEGIN {
print "时间范围 YGC 平均时间(s) FGC 平均时间(s) Old平均使用率 Eden峰值"
print "--------------------------------------------------------------------------------"
}
/YGC.*YGCT.*FGC.*FGCT/ {
# 跳过标题行
next
}
{
# 解析每行数据
split($0, fields, " ");
# 计算统计
ygc_sum += fields[7];
ygct_sum += fields[8];
fgc_sum += fields[9];
fgct_sum += fields[10];
old_sum += fields[4];
# 记录峰值
if (fields[3] > eden_peak) eden_peak = fields[3];
if (fields[4] > old_peak) old_peak = fields[4];
count++;
# 每10行输出一次统计
if (count % 10 == 0) {
avg_ygct = (ygct_sum - last_ygct) / (ygc_sum - last_ygc);
avg_fgct = (fgct_sum - last_fgct) / (fgc_sum - last_fgc);
avg_old = old_sum / 10;
printf "%s-%s %4d %8.3f %3d %8.3f %6.1f%% %6.1f%%\n",
start_time, fields[1],
ygc_sum - last_ygc, avg_ygct,
fgc_sum - last_fgc, avg_fgct,
avg_old, eden_peak;
last_ygc = ygc_sum;
last_ygct = ygct_sum;
last_fgc = fgc_sum;
last_fgct = fgct_sum;
old_sum = 0;
start_time = fields[1];
eden_peak = 0;
}
}
END {
print "\n=== 总结 ===";
printf "总运行时间: %d次采样\n", count;
printf "总YGC次数: %d,总时间: %.3fs,平均: %.3fs/次\n",
ygc_sum, ygct_sum, ygct_sum/ygc_sum;
printf "总FGC次数: %d,总时间: %.3fs,平均: %.3fs/次\n",
fgc_sum, fgct_sum, fgct_sum/fgc_sum;
printf "Old区平均使用率: %.1f%%,峰值: %.1f%%\n",
old_sum/count, old_peak;
printf "Eden区峰值: %.1f%%\n", eden_peak;
# 给出建议
print "\n=== 优化建议 ===";
if (fgct_sum/ygct_sum > 0.5) {
print "⚠️ Full GC时间占比过高,建议:";
print " 1. 检查内存泄漏";
print " 2. 调整新生代与老年代比例";
print " 3. 考虑使用G1 GC";
}
if (old_peak > 85) {
print "⚠️ 老年代使用率过高,建议:";
print " 1. 增加堆内存(-Xmx)";
print " 2. 降低对象晋升阈值(-XX:MaxTenuringThreshold)";
}
if (ygct_sum/ygc_sum > 0.1) {
print "⚠️ Young GC时间过长,建议:";
print " 1. 减小新生代大小";
print " 2. 检查创建大量临时对象的代码";
}
}' "$LOG_FILE"
4.3 告警触发脚本
#!/bin/bash
# jstat_alert.sh
PID=$1
THRESHOLD_OLD=${2:-80} # 老年代阈值,默认80%
THRESHOLD_META=${3:-90} # 元空间阈值,默认90%
ALERT_INTERVAL=${4:-60} # 告警间隔,默认60秒
LAST_ALERT_TIME=0
while true; do
# 获取jstat数据
JSTAT_OUTPUT=$(jstat -gcutil $PID 1000 1 | tail -1)
# 解析数据
OLD_USAGE=$(echo $JSTAT_OUTPUT | awk '{print $4}')
META_USAGE=$(echo $JSTAT_OUTPUT | awk '{print $5}')
FGC_COUNT=$(echo $JSTAT_OUTPUT | awk '{print $9}')
CURRENT_TIME=$(date +%s)
# 检查条件并告警
if (( $(echo "$OLD_USAGE > $THRESHOLD_OLD" | bc -l) )); then
if (( CURRENT_TIME - LAST_ALERT_TIME > ALERT_INTERVAL )); then
echo "[$(date)] ⚠️ 告警:老年代使用率过高 - ${OLD_USAGE}% (阈值: ${THRESHOLD_OLD}%)"
echo "进程: $PID,建议检查内存泄漏或增加堆内存"
LAST_ALERT_TIME=$CURRENT_TIME
fi
fi
if (( $(echo "$META_USAGE > $THRESHOLD_META" | bc -l) )); then
if (( CURRENT_TIME - LAST_ALERT_TIME > ALERT_INTERVAL )); then
echo "[$(date)] ⚠️ 告警:元空间使用率过高 - ${META_USAGE}% (阈值: ${THRESHOLD_META}%)"
echo "进程: $PID,建议增加-XX:MaxMetaspaceSize"
LAST_ALERT_TIME=$CURRENT_TIME
fi
fi
# 检查FGC次数突然增加
if [ -f /tmp/last_fgc_count ]; then
LAST_FGC=$(cat /tmp/last_fgc_count)
if (( FGC_COUNT > LAST_FGC + 2 )); then
echo "[$(date)] ⚠️ 告警:短时间内发生多次Full GC"
echo "当前FGC: $FGC_COUNT,上次: $LAST_FGC"
fi
fi
echo $FGC_COUNT > /tmp/last_fgc_count
sleep 5
done
五、与其它工具结合分析
5.1 结合jmap分析对象分布
# 当发现Old区使用率高时,分析对象分布
jmap -histo:live <pid> | head -20
# 输出示例:
num #instances #bytes class name
----------------------------------------------
1: 1234567 1073741824 [B # byte数组占用1GB
2: 987654 268435456 [Ljava.lang.Object; # Object数组
3: 500000 134217728 java.lang.String
5.2 结合jstack分析线程
# 当GC时间过长时,检查GC线程
jstack <pid> | grep -A 5 "GC"
# 输出示例:
"GC Thread#0" #12 daemon prio=2 os_prio=0 cpu=1234.56ms elapsed=12345.67s
5.3 结合监控系统(Prometheus)
# 将jstat数据转换为Prometheus格式
- pattern: 'jvm\.gc\.(\w+)\.time:(\d+\.\d+)'
name: jvm_gc_$1_seconds_total
type: COUNTER
help: Total time spent in $1 GC
- pattern: 'jvm\.gc\.(\w+)\.count:(\d+)'
name: jvm_gc_$1_total
type: COUNTER
help: Total number of $1 GCs
六、最佳实践总结
6.1 监控要点
- 定期采样:生产环境建议每分钟采样一次,持久化存储
- 关注趋势:不仅看当前值,更要关注变化趋势
- 设置基线:为每个应用建立正常的GC行为基线
- 关联分析:结合业务指标(QPS、响应时间)分析
6.2 关键阈值建议
| 指标 | 警告阈值 | 严重阈值 | 行动建议 |
|---|---|---|---|
| Old区使用率 | >70% | >85% | 检查内存泄漏 |
| Metaspace使用率 | >80% | >90% | 增加MaxMetaspaceSize |
| YGC平均时间 | >0.05s | >0.1s | 优化年轻代大小 |
| FGC频率 | >1次/小时 | >1次/10分钟 | 紧急优化 |
| GC时间占比 | >5% | >10% | 全面GC调优 |
6.3 常见优化方向
- 调整堆大小:
-Xms、-Xmx、-Xmn - 调整Survivor比例:
-XX:SurvivorRatio - 调整晋升阈值:
-XX:MaxTenuringThreshold - 更换GC算法:Parallel、CMS、G1、ZGC
- 优化代码:减少大对象、及时释放资源
通过熟练使用jstat命令,结合其他工具和监控系统,可以有效诊断和解决Java内存问题,保障应用稳定运行。
珊瑚海

浙公网安备 33010602011771号