内存泄露造成的表现可能有:
- 应用程序长时间连续运行时性能严重下降
- CPU使用率飙升,甚至到100%
- 频繁
Full GC
,各种报警,例如接口超时报警等 - 应用程序抛出
OutOfMemoryError
错误 - 应用程序偶尔会耗尽连接对象
定位过程
严重内存泄漏往往伴随频繁的 Full GC,所以分析排查内存泄漏问题首先还得从查看Full GC入手。主要有以下操作步骤
- 使用
jps
查看运行的 Java 进程 ID - 使用
top -p [pid]
查看进程使用 CPU 和 MEM 的情况 - 使用
top -Hp [pid]
查看进程下的所有线程占 CPU 和 MEM 的情况 - 将线程 ID 转换为 16 进制:
printf "%x\n" [pid]
,输出的值就是线程栈信息中的 nid。
例如:printf "%x\n" 29471
,换行输出731f
。 - 抓取线程栈:
jstack 29452 > 29452.txt
,可以多抓几次做个对比。
在线程栈信息中找到对应线程号的 16 进制值,如下是731f
线程的信息。线程栈分析可使用 Visualvm 插件 TDA。
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fbe2c164000 nid=0x731f runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
- 使用
jstat -gcutil [pid] 5000 10
每隔 5 秒输出GC信息,输出10次,查看YGC和Full GC 次数。通常会出现YGC不增加或增加缓慢,而 Full GC增加很快。
或使用jstat -gccause [pid] 5000
,同样是输出GC摘要信息。
或使用jmap -heap [pid]
查看堆的摘要信息,关注老年代内存使用是否达到阀值,若达到阀值就会执行 Full GC。 - 如果发现
Full GC
次数太多,就很大概率存在内存泄漏了 - 使用
jmap -histo:live [pid]
输出每个类的对象数量,内存大小(字节单位)及全限定类名。 - 生成
dump
文件,借助工具分析哪个对象非常多,基本就能定位到问题在哪了
使用 jmap 生成 dump 文件:
# jmap -dump:live,format=b,file=29471.dump 29471
Dumping heap to /root/dump ...
Heap dump file created
可以使用 jhat 命令分析:jhat -port 8000 29471.dump
,浏览器访问 jhat 服务,端口是 8000。
通常使用图形化工具分析,如 JDK 自带的 jvisualvm,从菜单 > 文件 > 装入 dump 文件。
或使用第三方式具分析的,如 JProfiler 也是个图形化工具,GCViewer 工具。Eclipse 或以使用 MAT 工具查看。或使用在线分析平台 GCEasy。
注意:如果 dump 文件较大的话,分析会占比较大的内存。
基本上就可以定位到代码层的逻辑了。
- 在 dump 文析结果中查找存在大量的对象,再查对其的引用。
- dump 文件分析