零基础学习性能测试第二章-JVM如何监控
目录
以下是专为零基础设计的JVM监控实战指南,聚焦关键指标、常用工具和快速定位问题的方法,可直接应用于性能测试工作:
一、JVM监控核心指标(先记这6个)
jstat -gc
jstat -gcutil
jstack
top
+ jstack
二、必备监控工具与速查命令
1. 实时监控三件套
# 1. 监控GC状态 (每2秒刷新)jstat -gcutil <PID> 2000# 输出解读:# S0 S1 E O M CCS YGC YGCT FGC FGCT GCT# 0.0 0.0 65.0 25.0 95.0 90.0 100 1.5 5 0.8 2.3# ▶️ E: Eden区使用率 | O: 老年代 | FGC: Full GC次数 | FGCT: Full GC总耗时# 2. 查看JVM参数 (关键配置)jcmd <PID> VM.flags# 3. 生成线程快照 (分析死锁/阻塞)jstack <PID> > thread_dump.txt
2. GC日志配置(必须开启!)
// JDK 8及之前-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log// JDK 11+ (推荐)-Xlog:gc*:file=/path/to/gc.log:time:filecount=0
日志分析工具:
- GCeasy(在线分析)
grep \"Full GC\" gc.log
(手动检查Full GC)
三、4种内存泄漏快速定位法
场景:堆内存持续增长不释放
-
步骤1:确认泄漏
jmap -heap <PID> | grep \"used\" # 观察Old Gen使用趋势
-
步骤2:生成堆转储
jmap -dump:live,format=b,file=heapdump.hprof <PID>
-
步骤3:分析堆快照
- 工具:Eclipse MAT / VisualVM
- 定位技巧:
- 查找 Retained Heap 最大的对象
- 检查 Unreachable Objects(不可达对象)
- 查看 Dominator Tree(支配树)
-
常见泄漏模式:
#mermaid-svg-LagfsrQssErit379 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LagfsrQssErit379 .error-icon{fill:#552222;}#mermaid-svg-LagfsrQssErit379 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LagfsrQssErit379 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-LagfsrQssErit379 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LagfsrQssErit379 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LagfsrQssErit379 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LagfsrQssErit379 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LagfsrQssErit379 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LagfsrQssErit379 .marker.cross{stroke:#333333;}#mermaid-svg-LagfsrQssErit379 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LagfsrQssErit379 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LagfsrQssErit379 .cluster-label text{fill:#333;}#mermaid-svg-LagfsrQssErit379 .cluster-label span{color:#333;}#mermaid-svg-LagfsrQssErit379 .label text,#mermaid-svg-LagfsrQssErit379 span{fill:#333;color:#333;}#mermaid-svg-LagfsrQssErit379 .node rect,#mermaid-svg-LagfsrQssErit379 .node circle,#mermaid-svg-LagfsrQssErit379 .node ellipse,#mermaid-svg-LagfsrQssErit379 .node polygon,#mermaid-svg-LagfsrQssErit379 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LagfsrQssErit379 .node .label{text-align:center;}#mermaid-svg-LagfsrQssErit379 .node.clickable{cursor:pointer;}#mermaid-svg-LagfsrQssErit379 .arrowheadPath{fill:#333333;}#mermaid-svg-LagfsrQssErit379 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LagfsrQssErit379 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LagfsrQssErit379 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-LagfsrQssErit379 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-LagfsrQssErit379 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LagfsrQssErit379 .cluster text{fill:#333;}#mermaid-svg-LagfsrQssErit379 .cluster span{color:#333;}#mermaid-svg-LagfsrQssErit379 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LagfsrQssErit379 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} 内存泄漏 静态集合未清理 线程池未销毁 缓存未设TTL 资源未关闭
四、GC性能调优实战
高频Full GC问题排查
# 1. 检查GC原因grep -A 5 \"Full GC\" gc.log# 典型原因:# - \"Allocation Failure\" -> Eden区不足# - \"Metadata GC Threshold\" -> 元空间不足# - \"System.gc()\" -> 代码调用System.gc()# 2. 优化方案| 问题现象 | 参数调整 ||------------------------|--------------------------|| Young GC频繁 | -Xmn增加年轻代大小 || Full GC后堆内存未释放 | -XX:+UseCMSCompactAtFullCollection || CMS GC失败 | -XX:CMSInitiatingOccupancyFraction=70 || G1混合GC耗时过长 | -XX:G1MixedGCLiveThresholdPercent=85 |
五、线程问题定位技巧
1. 线程数暴涨
# 统计线程数ps -eLf | grep java | wc -l# 分析线程类型jstack <PID> | grep \"java.lang.Thread.State\" | sort | uniq -c
2. 死锁检测
jstack <PID> | grep -A 10 \"deadlock\"
3. CPU飙高分析
# 1. 找高CPU线程top -H -p <PID> # 记录线程ID(十进制)# 2. 线程ID转十六进制printf \"%x\\n\" 12345 # 输出:3039# 3. jstack中搜索nid=0x3039jstack <PID> | grep -A 20 \"nid=0x3039\"
六、生产级监控方案(Prometheus + Grafana)
1. 监控体系搭建
#mermaid-svg-jmZjH3OpkGVYZdqx {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx .error-icon{fill:#552222;}#mermaid-svg-jmZjH3OpkGVYZdqx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jmZjH3OpkGVYZdqx .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-jmZjH3OpkGVYZdqx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jmZjH3OpkGVYZdqx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jmZjH3OpkGVYZdqx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jmZjH3OpkGVYZdqx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jmZjH3OpkGVYZdqx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jmZjH3OpkGVYZdqx .marker.cross{stroke:#333333;}#mermaid-svg-jmZjH3OpkGVYZdqx svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jmZjH3OpkGVYZdqx .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx .cluster-label text{fill:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx .cluster-label span{color:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx .label text,#mermaid-svg-jmZjH3OpkGVYZdqx span{fill:#333;color:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx .node rect,#mermaid-svg-jmZjH3OpkGVYZdqx .node circle,#mermaid-svg-jmZjH3OpkGVYZdqx .node ellipse,#mermaid-svg-jmZjH3OpkGVYZdqx .node polygon,#mermaid-svg-jmZjH3OpkGVYZdqx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jmZjH3OpkGVYZdqx .node .label{text-align:center;}#mermaid-svg-jmZjH3OpkGVYZdqx .node.clickable{cursor:pointer;}#mermaid-svg-jmZjH3OpkGVYZdqx .arrowheadPath{fill:#333333;}#mermaid-svg-jmZjH3OpkGVYZdqx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jmZjH3OpkGVYZdqx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jmZjH3OpkGVYZdqx .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-jmZjH3OpkGVYZdqx .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-jmZjH3OpkGVYZdqx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jmZjH3OpkGVYZdqx .cluster text{fill:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx .cluster span{color:#333;}#mermaid-svg-jmZjH3OpkGVYZdqx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jmZjH3OpkGVYZdqx :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;} JMX Metrics Dashboard JVM JMX Exporter Prometheus Grafana
2. 关键Dashboard指标
- 内存:Heap/Non-Heap Memory Usage
- GC:GC Pause Duration, GC Count
- 线程:Thread States, Deadlock Detection
- 类加载:Loaded Classes Count
3. 告警规则示例
# Prometheus告警规则- alert: FullGCTooFrequent expr: increase(jvm_gc_pause_seconds_count{gc=\"PS MarkSweep\"}[1m]) > 2 for: 5m labels: severity: critical annotations: summary: \"Full GC频繁 (实例 {{ $labels.instance }})\"
七、性能测试JVM监控SOP
-
压测前准备
# 开启GC日志java -Xlog:gc*:file=gc.log -jar app.jar# 添加OOM自动dump-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp
-
压测中监控
# 实时看板jstat -gcutil <PID> 1000 # 每秒刷新jcmd <PID> Thread.print # 定时抓线程
-
压测后分析
# 1. GC分析cat gc.log | grep \"Full GC\" | wc -l # 统计Full GC次数# 2. 内存泄漏检查jmap -histo:live <PID> | head -20 # 对象直方图# 3. 生成报告echo \"=== JVM健康报告 ===\"echo \"Full GC次数: $(grep \'Full GC\' gc.log | wc -l)\"echo \"最大堆内存: $(jstat -gccapacity <PID> | awk \'{print $4/1024\"MB\"}\')\"
八、避坑指南(血泪经验)
-
元空间溢出
-XX:MaxMetaspaceSize=256m
# 默认无限制需手动设置 -
堆外内存泄漏
监控Native Memory Tracking (NMT)
:-XX:NativeMemoryTracking=detailjcmd <PID> VM.native_memory summary
-
G1并发模式失败
增加-XX:ConcGCThreads
或降低-XX:InitiatingHeapOccupancyPercent
-
线程池耗尽
jstack
中搜索\"pool-1-thread\"
查看阻塞栈
终极命令:一键式JVM健康检查
#!/bin/bashPID=$(jps | grep YourApp | awk \'{print $1}\')echo \"===== JVM健康检查 =====\"echo \"1. GC状态:\"jstat -gcutil $PID 1000 3echo \"2. 堆内存:\"jmap -heap $PID | grep -E \"Heap Usage|New Generation\"echo \"3. 线程快照:\"jstack $PID | grep \"java.lang.Thread.State\" | sort | uniq -c
掌握这些技能,你可以在10分钟内完成JVM基础问题定位,有效支撑性能测试工作。记住核心口诀:
“内存泄漏查堆dump,GC瓶颈看日志,线程问题抓jstack,生产监控上Prometheus”