内存泄露造成的表现可能有:

  • 应用程序长时间连续运行时性能严重下降
  • CPU使用率飙升,甚至到100%
  • 频繁Full GC,各种报警,例如接口超时报警等
  • 应用程序抛出OutOfMemoryError错误
  • 应用程序偶尔会耗尽连接对象

定位过程

严重内存泄漏往往伴随频繁的 Full GC,所以分析排查内存泄漏问题首先还得从查看Full GC入手。主要有以下操作步骤

  1. 使用jps查看运行的 Java 进程 ID
  2. 使用top -p [pid]查看进程使用 CPU 和 MEM 的情况
  3. 使用top -Hp [pid]查看进程下的所有线程占 CPU 和 MEM 的情况
  4. 将线程 ID 转换为 16 进制:printf "%x\n" [pid],输出的值就是线程栈信息中的 nid。
    例如:printf "%x\n" 29471,换行输出731f
  5. 抓取线程栈: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
  1. 使用jstat -gcutil [pid] 5000 10每隔 5 秒输出GC信息,输出10次,查看YGC和Full GC 次数。通常会出现YGC不增加或增加缓慢,而 Full GC增加很快。
    或使用jstat -gccause [pid] 5000,同样是输出GC摘要信息。
    或使用jmap -heap [pid]查看堆的摘要信息,关注老年代内存使用是否达到阀值,若达到阀值就会执行 Full GC。
  2. 如果发现Full GC次数太多,就很大概率存在内存泄漏了
  3. 使用jmap -histo:live [pid]输出每个类的对象数量,内存大小(字节单位)及全限定类名。
  4. 生成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 文件分析