内存泄漏问题排查

前言

在故障定位(尤其是Out Of Memory)和性能分析的时候,我们经常会用一些文件辅助我们排查代码问题,这些文件记录了JVM运行期间的 内存占用、线程执行 等情况,这就是我们常说的dump文件。常用的有 heap dumpthread dump,我们可以理解为heap dump是记录内存信息的,thread dump是记录CPU信息的。

概念

(1)heap dump:此文件是一个二进制文件,它保存了某一时刻 JVM 堆中对象使用情况,它是指定时刻的JAVA堆栈的快照,是一种镜像文件。

(2)jmap:是一个可以输出内存中对象的工具,它位于JDK目录里的bin文件夹下。

(3)thread dump:它保存了某一时刻线程活动的快照,及JVM中所有java线程的堆栈跟踪信息,堆栈信息一般包含完整的类名及所执行的方法,还可能有源代码的行数。

(4)jstack:是一个抓取 thread dump 文件的有效的命令行工具,它位于JDK目录里的bin文件夹下。

Heap Dump 包含的信息

(1)所有的对象信息:对象的类信息、字段信息、原生值(int, long等)及引用值

(2)所有的类信息:类加载器、类名、超类及静态字段

(3)垃圾回收的根对象:根对象是指那些可以直接被虚拟机触及的对象

(4)线程栈及局部变量:包含了转储时刻的线程调用栈信息和栈帧中的局部变量信息

一. 进程分析(thread dump)

1. 在 windows 下查看占用内存最多的java线程的 tid

(1)使用Process Explorer 工具:http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

内存泄漏问题排查_第1张图片
获取线程tid

(2)找到 IEDA 运行的 java 程序,点击properties 显示进程详情,可以看到此进程的 pid 为 4016(如上图3所示),然后可以查看占用内存和cpu最多的线程为17072(如上图4所示)

2. 进入jdk安装目录 C:\Program Files\Java\jdk1.8.0_211\bin

3. 使用命令:jstack -l 4016 > c:/dumptest/4016.stack 导出进程4016的 thread dump 快照

4. 占用内存最多的线程17072的十六进制为42b0,打开上一步导出的4016.stack快照,搜索 42b0,可知占用CPU最高的线程为垃圾回收线程

内存泄漏问题排查_第2张图片
GC线程

5. 到这一步,就要分析为何频繁GC,一般是由某个对象占用了大量内存无法回收和代码中有死循环导致,接下来就要用到内存分析了,使用内存分析查看哪些对象占用了大量内存

二. 内存分析(heap dump)

1. Heap Dump 文件获取

(1)使用jmap命令生成dump文件

        a. 在windows主机上通过 tasklist 查看java进程id

        b. 进入jdk安装目录 C:\Program Files\Java\jdk1.8.0_211\bin

        c. 使用命令 jmap -dump:live,format=b,file=c:\dumptest\heap-dump.hprof 生成heap dump文件

(2)使用jcmd命令生成dump文件

        jcmd GC.head_dump c:\dump\heap-dump.hprof

(3)使用JVM参数获取dump文件

        a. JVM配置中添加-XX:+HeapDumpOnOutOfMemoryError,当OutOfMemoryError发时自动生成heap dump文件

        b. -XX:+HeapDumpBeforeFullGC,当 JVM 执行 FullGC 前执行 dump

        c. -XX:+HeapDumpAfterFullGC,当 JVM 执行 FullGC 后执行 dump

        d. -XX:+HeapDumpOnCtrlBreak,交互式获取dump。在控制台按下快捷键Ctrl + Break时,JVM就会转存一下堆快照

        e. -XX:HeapDumpPath=c:\test.hprof,指定 dump 文件存储路径

        注意:JVM 生成 Heap Dump 的时候,虚拟机是暂停一切服务的。如果是线上系统执行 Heap Dump 时需要注意。

2. Heap Dump 文件分析

(1)分析工具:JProfiler

(2)下载地址:https://www.jb51.net/softs/608640.html

(3)打开软件,点击左上角 Start Center——Open Snapshots——Open Single Snapshot,导入hprof格式的heap dump文件

(4)查看 biggest objects 分析对象的内存占比

三. 性能分析

1. 常用方法:

(1)使用 jstat -gcutil 观察两次FGC之后老年代(O)使用比例;

(2)如果两次FGC后老年代使用比例任然居高不下,说明有大对象常驻内存;

(3)然后使用 jmap -histo:live 72947 | more 查看该对象大小;

2. 参数解析:

总结垃圾回收统计(各区域使用比例)

(1)java堆分为新生代老年代,新生代为 Eden + From Survivor + To Survivor,如上图中的 E + S0 + S1,Eden 和 Survivor 的内存比为8:1,每次只使用一个Eden 和一个 Survivor 区域,另一个 Survivor 用于复制收集算法回收内存;

(2)O(Old)老年代,一般大对象(长字符串和大数组)直接分配在老年代中,同时“年龄”长的的对象会从新生代自动晋升到老年代中;

(3)P(Permanent)永久代,方法区称为永久代;

(4)YGC 和 YGCT 分别代表年轻代回收次数和回收耗时;

(5)FGC 和 FGCT 分别代表老年代回收次数和回收耗时;

(6)GCT 代表总的垃圾回收耗时;

(7)当 Eden 区分配不足时,会自动发生一次 Minor GC,当发生 Minor GC 时,虚拟机会自动检测(比较)新生代晋升到老年代的对象内存大小和老年代剩余内存大小:

        a. 如果 晋升 > 剩余,则发生一次Full GC

        b. 如果 晋升 < 剩余,则去检测老年代的内存担保 HandlePromotionFailure 是否允许担保失败,如果不允许担保失败,则发生一次 Full GC,如果允许失败,则进行一次Minor GC。

你可能感兴趣的:(内存泄漏问题排查)