跳到主要内容

JVM 监控与诊断

JVM监控与诊断是保障Java应用稳定运行的重要手段。通过监控JVM的运行状态,可以及时发现性能问题、内存泄漏等问题,并进行有效诊断。

为什么需要JVM监控?

监控的价值

监控指标

指标类别具体指标说明
内存堆内存使用、非堆内存使用内存是否充足
GCGC次数、GC时间、GC原因GC是否健康
线程线程数、线程状态、死锁线程是否正常
类加载加载类数、卸载类数类加载是否正常
CPUCPU使用率计算资源是否充足

JDK自带工具

jps - JVM进程状态工具

# 列出所有Java进程
jps

# 显示完整包名
jps -l

# 显示传递给main方法的参数
jps -m

# 显示JVM参数
jps -v

# 组合使用
jps -lmv

输出示例

12345 com.example.Application --spring.profiles.active=prod
12346 sun.tools.jps.Jps -lmv

jstat - JVM统计信息监控

# 查看GC统计信息
jstat -gc <pid> 1000

# 查看GC汇总
jstat -gcutil <pid> 1000

# 查看类加载统计
jstat -class <pid>

# 查看编译统计
jstat -compiler <pid>

输出解读

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
512.0 512.0 0.0 448.0 4096.0 2048.0 10240.0 5120.0 4864.0 4608.0 512.0 480.0 12 0.150 2 0.500 0.650

S0C/S1C: Survivor区容量
S0U/S1U: Survivor区使用量
EC/EU: Eden区容量/使用量
OC/OU: 老年代容量/使用量
MC/MU: 元空间容量/使用量
YGC: Young GC次数
YGCT: Young GC时间
FGC: Full GC次数
FGCT: Full GC时间
GCT: 总GC时间

jmap - 内存映射工具

# 生成堆转储文件
jmap -dump:format=b,file=heap.hprof <pid>

# 查看堆配置
jmap -heap <pid>

# 查看对象统计(直方图)
jmap -histo <pid> | head -30

# 查看类加载器统计
jmap -clstats <pid>

应用场景

# 1. 发现内存泄漏时生成堆转储
jmap -dump:live,format=b,file=heap.hprof <pid>

# 2. 分析内存占用
jmap -histo:live <pid> | head -20

jstack - 线程堆栈工具

# 打印线程堆栈
jstack <pid>

# 检测死锁
jstack -l <pid>

# 输出到文件
jstack <pid> > thread_dump.txt

输出解读

"main" #1 prio=5 os_prio=0 cpu=1000.00ms elapsed=100.00s tid=0x00007f8b4c001800 nid=0x1234 waiting on condition  [0x00007f8b4c001000]
java.lang.Thread.State: WAITING (parking)
at jdk.internal.misc.Unsafe.park([email protected]/Native Method)
- parking to wait for <0x000000076b5c7d58> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park([email protected]/LockSupport.java:341)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionNode.block([email protected]/AbstractQueuedSynchronizer.java:506)

线程名: main
线程ID: #1
优先级: 5
线程状态: WAITING (parking)
当前方法: Unsafe.park
等待对象: AbstractQueuedSynchronizer$ConditionObject

jinfo - 配置信息工具

# 查看JVM参数
jinfo -flags <pid>

# 查看系统属性
jinfo -sysprops <pid>

# 查看特定参数
jinfo -flag MaxHeapSize <pid>

# 动态修改参数(仅部分参数支持)
jinfo -flag +PrintGCDetails <pid>

jcmd - 多功能诊断工具

# 列出所有Java进程
jcmd -l

# 查看进程帮助
jcmd <pid> help

# 查看VM信息
jcmd <pid> VM.version
jcmd <pid> VM.flags
jcmd <pid> VM.command_line

# 查看线程信息
jcmd <pid> Thread.print

# 生成堆转储
jcmd <pid> GC.heap_dump filename=heap.hprof

# 查看GC信息
jcmd <pid> GC.run
jcmd <pid> GC.class_histogram

可视化工具

VisualVM

JDK自带的可视化监控工具:

# 启动VisualVM
jvisualvm

# 或
visualvm

功能

  • 实时监控CPU、内存、GC
  • 线程监控和死锁检测
  • 堆转储分析
  • 性能分析(Profiler)

JConsole

JMX监控工具:

# 启动JConsole
jconsole

# 连接远程进程
jconsole service:jmx:rmi:///jndi/rmi://host:port/jmxrmi

监控页面

  • 概览:CPU、内存、线程、类
  • 内存:堆、非堆、内存池
  • 线程:线程数、死锁检测
  • 类:加载类数
  • VM摘要:JVM信息

Java Mission Control (JMC)

Oracle提供的高级监控工具:

# 启动JMC
jmc

功能

  • 低开销的性能分析
  • 飞行记录器(Flight Recorder)
  • 实时内存分析
  • 线程分析

第三方工具

Arthas

阿里开源的Java诊断工具:

# 下载并启动
wget https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 常用命令
dashboard # 系统仪表盘
thread # 线程信息
jvm # JVM信息
memory # 内存信息
gc # GC信息

# 方法诊断
watch com.example.Service getOrder '{params,returnObj,throwExp}' -x 2
trace com.example.Service getOrder
stack com.example.Service getOrder

# 反编译
jad com.example.Service

# 查看方法入参和返回值
tt -t com.example.Service getOrder

MAT (Memory Analyzer Tool)

Eclipse提供的堆转储分析工具:

# 分析堆转储文件
./ParseHeapDump.sh heap.hprof

功能

  • 查找内存泄漏嫌疑
  • 分析对象引用链
  • 计算对象保留大小
  • 生成泄漏报告

async-profiler

低开销的Java性能分析工具:

# CPU分析
./profiler.sh -d 30 -f cpu.html <pid>

# 内存分配分析
./profiler.sh -d 30 -e alloc -f alloc.html <pid>

# 锁竞争分析
./profiler.sh -d 30 -e lock -f lock.html <pid>

监控平台集成

Prometheus + Grafana

// 添加依赖
// micrometer-registry-prometheus

// 配置Prometheus端点
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config()
.commonTags("application", "myapp");
}

监控指标

  • jvm_memory_used_bytes
  • jvm_gc_pause_seconds
  • jvm_threads_live_threads
  • jvm_classes_loaded_classes

Elastic APM

# 启动Java应用时添加agent
java -javaagent:/path/to/elastic-apm-agent.jar \
-Delastic.apm.service_name=my-service \
-Delastic.apm.server_url=http://apm-server:8200 \
-jar myapp.jar

常见问题的诊断

内存泄漏诊断

步骤

  1. 确认问题

    jstat -gcutil <pid> 1000
    # 观察老年代使用率是否持续增长
  2. 生成堆转储

    jmap -dump:live,format=b,file=heap.hprof <pid>
  3. 分析堆转储

    • 使用MAT分析 dominator_tree
    • 查找保留大小最大的对象
    • 分析引用链
  4. 定位代码

    // 常见内存泄漏场景
    public class MemoryLeakExample {
    private static final List<Object> cache = new ArrayList<>();

    public void addToCache(Object obj) {
    cache.add(obj); // 对象永远不会被释放
    }
    }

线程死锁诊断

步骤

  1. 检测死锁

    jstack -l <pid> | grep -A 20 "Found one Java-level deadlock"
  2. 分析死锁

    Found one Java-level deadlock:
    =============================

    "Thread-1":
    waiting to lock monitor 0x00007f8b4c001000 (object 0x000000076b5c7d58)
    which is held by "Thread-2"
    "Thread-2":
    waiting to lock monitor 0x00007f8b4c002000 (object 0x000000076b5c7d60)
    which is held by "Thread-1"
  3. 解决死锁

    • 统一加锁顺序
    • 使用tryLock
    • 避免嵌套锁

CPU过高诊断

步骤

  1. 找到CPU高的进程

    top
  2. 找到CPU高的线程

    top -H -p <pid>
  3. 转换线程ID

    printf "%x\n" <thread_id>
  4. 查看线程堆栈

    jstack <pid> | grep -A 20 <hex_thread_id>
  5. 使用async-profiler

    ./profiler.sh -d 30 -f cpu.html <pid>

GC问题诊断

步骤

  1. 分析GC日志

    # 使用GC日志分析工具
    java -jar gceasy.jar gc.log
  2. 常见GC问题

    • 频繁Full GC:老年代空间不足
    • GC时间长:堆内存过大或收集器选择不当
    • 内存泄漏:对象无法被回收
  3. 优化策略

    # 增大堆内存
    -Xmx4g -Xms4g

    # 使用G1收集器
    -XX:+UseG1GC
    -XX:MaxGCPauseMillis=200

监控最佳实践

1. 建立监控基线

# 收集正常运行时的指标
jstat -gcutil <pid> 60000 > baseline_gc.log

2. 设置告警阈值

指标警告阈值严重阈值
堆内存使用率70%85%
GC时间占比5%10%
Full GC频率1次/小时1次/10分钟
线程数200500

3. 定期生成堆转储

# 定时任务生成堆转储
0 2 * * * jmap -dump:live,format=b,file=/backup/heap_$(date +\%Y\%m\%d).hprof <pid>

4. 保留GC日志

# 配置GC日志滚动
-Xlog:gc*:file=/var/log/gc.log::filecount=5,filesize=100m

小结

JVM监控与诊断是保障应用稳定运行的重要手段:

  1. 命令行工具:jps、jstat、jmap、jstack、jinfo、jcmd
  2. 可视化工具:VisualVM、JConsole、JMC
  3. 第三方工具:Arthas、MAT、async-profiler
  4. 监控平台:Prometheus、Elastic APM
  5. 问题诊断:内存泄漏、线程死锁、CPU过高、GC问题

建立完善的监控体系,可以及时发现和解决问题,保障应用稳定运行。

参考资料