《JVM剖析及性能跟踪》

案例2--jstack+MemoryAnalyzer分析Java进程cpu高

正文

问题描述

线上服务器的CPU使用率自某日起逐渐升高,最终达到100%,导致服务不可用。重启机器后问题得以解决。

image2021-3-25_16-39-25.png

解决方案

为了快速恢复业务,重启了6台服务器中问题较为严重的5台,保留一台服务器进行问题分析。这一步至关重要,因为它不仅恢复了业务,还为后续排查提供了现场环境。

步骤一:定位高CPU进程

使用top命令发现Java进程(PID 384)CPU占用异常高。

image2021-3-25_16-56-4.png

步骤二:查找高CPU线程

使用top -Hp 384命令发现Java线程4430、4431、4432、4433各自占用了约40%的CPU,消耗光了CPU。

image2021-3-25_16-57-22.png

步骤三:转换线程ID

将线程ID转换为16进制:

printf "%x\n" 4430 4431 4432 4433

结果分别为114e、114f、1150、1151。

步骤四:分析线程堆栈

使用jstack -l 384 > /1.txt导出所有线程的堆栈信息,并查找这些线程的堆栈。发现这些线程均为GC线程,表明JVM内存不足,频繁进行垃圾回收,导致CPU高负荷。

image2021-3-25_16-59-34.png

步骤五:dump堆内存并分析

使用jmap命令导出堆内存数据:

sudo -u tomcat jmap -dump:live,format=b,file=/dump.dat 384

使用MemoryAnalyzer (MAT)加载堆文件,发现javax.crypto.JceSecurity对象占用了95%的内存空间。进一步分析引用树,发现BouncyCastleProvider对象持有过多,表明代码中对该对象的处理方式存在问题。需要优化我们自己的业务代码,目前已定位到问题。

image2021-3-25_17-2-44.pngimage2021-3-25_17-3-2.png

结论与建议

问题的根本原因在于内存不足,导致频繁GC,而具体原因是代码中对BouncyCastleProvider的不当使用,导致内存泄漏或占用过多。建议检查代码中对该对象的使用方式,确保正确释放资源,避免内存泄漏。

通过这一案例,我们学会了使用jstack和MemoryAnalyzer工具来分析Java进程的CPU高负荷问题,并掌握了从GC线程和堆内存分析中定位问题根源的方法。