Spring Cloud Config内存泄漏导致CPU100%问题排查
文章目录
前段时间,作为工具人的我被要求排查某台服务器CPU 100%
。当时听了内心狂喜,终于让我遇到CPU 100%问题了。但是,当时服务器着急重启,没有足够的时间给我排查问题,但是我还是在重启之前把堆Dump文件保存了下来。
这两天忙里偷闲就想着分析一下原因。
问题
CPU 100%
虽然之前没遇到过CPU 100%的问题吧,但是,这个问题该怎么查心里还是有数的。
使用
top
命令找到吃CPU的Java进程pid。查到pid为:31387,CPU 接近400%,几乎4个核全吃满了。
查询耗CPU的线程id。
1
top -Hp 31387
这里,查到了4个线程,每个线程都吃满了一个核接近100%。
选取一个线程id:31390。
线程id转16进制。
1
printf "%x\n" 31390
得到结果:7a9e
输出线程堆栈信息。
1
jstack 31387 | grep 7a9e -A 60|less
结果忘截图了。不过结果很明显,4个线程都是垃圾收集线程。
注意
执行jstack的用户需要与启动Java进程的用户一致。可通过以下命令查看启动Java进程的用户。
|
|
垃圾收集
所以,究竟是为什么导致垃圾收集吃满了CPU呢?
|
|
这个我截图了。
Eden
和Old
全满了,这很明显是内存泄漏了。
但是,内存泄漏的原因是啥呢?
|
|
我将堆dump文件保存了下来。
排查
大dump文件分析
当时Dump文件保存下来之后,一看大小。好家伙,7G
多。然后我就面临一个问题,没法将这种大文件从服务器上拿下来。所以当时我就立马进行了打包压缩。
|
|
打包之后,就只有1G
多一点,然后我就从服务器上将压缩包拿到本地然后解压。
开始了漫长的分析过程。
jvisualvm
我知道JDK的bin目录下有个叫jvisualvm
的程序是可以分析堆dump文件的。
直接点击菜单栏的文件->装入
将dump.txt加载进去。
注意
这里不要使用VM 核心 dump
载入,不然会报错:无效的核心dump文件
但是,由于我们的dump文件太大了,所以会报OOM。所以需要修改jvisualvm
的配置。
配置文件在%JAVA_HOME%\lib\visualvm\etc\visualvm.conf
。
我将配置修改为-J-Xms2048m -J-Xmx2048m
,修改之后就可以将dump文件加载进去,但是,很慢
。基本上无法进行分析工作。
jhat
|
|
我电脑一共才8G内存,不管我咋调内存参数都会OOM。我电脑可用内存太小了,看来分析需要长驻内存的数据已经大于可用内存,内存置换解决不了问题。
MAT
因为用上面两个工具都太慢了,所以,这次我直接使用Linux版的MAT
,直接在服务器上分析,借助服务器的强大性能,加快分析的速度。
下载Linux版MAT。
将MAT传到服务器解压后,执行以下命令。
1
./mat/ParseHeapDump.sh dump.txt org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
然后将结果中的
dump_Leak_Suspects.zip
、dump_System_Overview.zip
和dump_Top_Components.zip
下载到本地分析。
这里可能会有疑问,org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
这些参数是啥,我怎么知道要这么填参数。
附上贴心的传送门:Batch mode
注意
因为环境的原因,可能需要配置MemoryAnalyzer.ini
。
比如:我添加了-vm
配置,将其配置为JDK的bin目录。另外,将内存直接给了8g。
详细配置传送门:Memory Analyzer Configuration
内存泄漏分析报告
|
|
很明显,就是DeleteOnExitHook
的静态变量的这个LinkedHashSet
内存泄漏了。
另外,除了在服务器上分析。我又在本地机器上使用Windows
版的MAT分析了试试。
结果出乎我的意料:完全没问题。
MAT,YYDS。
大胆猜测jvisualvm
不行,MAT
行! 的原因是:
MAT
分析会生成文件落到磁盘,所以没有jvisualvm
那么吃内存。
解决办法
其实,知道有内存泄漏的问题的时候,我就知道是因为Spring Cloud Config
版本太低了。之前升级过,但是,这台服务器的Spring Cloud Config
一直是弃用的还是跑的老版本,只要升级版本即可。
在Github上对于这个内存泄漏问题也有相关issue。
MemoryLeak related to the HealthCheck
另外,这不是只有Spring Cloud Config
会出现这种问题,因为究其原因还是File.deleteOnExit API 存在缺陷。
详见:JDK-4872014
|
|
File.deleteOnExit
的作用就是在JVM退出的时候删除文件。
调用File.deleteOnExit
会将路径添加到DeleteOnExitHook
中的LinkedHashSet里面,如果不断添加,那么就会内存溢出。
所以File.deleteOnExit
不能随便作为File.delete()
的候选方法,比如:我怕File.delete()
没删掉,然后调用File.deleteOnExit
。这么做,如果是会创建大量文件的话,那么就很容易把内存撑爆了。