一、分析GC日志
/** * @author : Hejinsheng * @date : 2019/1/18 0018 * @Description: 模拟FULL GC/YOUNG GC * -Xms100M -Xmx100M -Xmn32m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError */ public class GCTest { public static void main(String[] args) { List<Object> list = new ArrayList<>(); for(int i=0;;i++){ System.out.println(i); list.add(new byte[1024]); } } }
打印的GC信息
[Full GC (Ergonomics) [PSYoungGen: 26624K->26623K(29696K)] [ParOldGen: 69631K->69630K(69632K)] 96255K->96253K(99328K), [Metaspace: 3298K->3298K(1056768K)], 0.0200149 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
[Full GC (Ergonomics) [PSYoungGen: 26624K->26623K(29696K)] [ParOldGen: 69631K->69631K(69632K)] 96255K->96255K(99328K), [Metaspace: 3298K->3298K(1056768K)], 0.0177546 secs] [Times: user=0.08 sys=0.00, real=0.02 secs]
[Full GC (Allocation Failure) [PSYoungGen: 26623K->26623K(29696K)] [ParOldGen: 69631K->69613K(69632K)] 96255K->96236K(99328K), [Metaspace: 3298K->3298K(1056768K)], 0.0466197 secs] [Times: user=0.03 sys=0.00, real=0.05 secs]
1. 最前面的数字217.539:代表GC发生的时间,从虚拟机启动开始算起
2. GC日志开头的[GC和FULL GC]说明这次垃圾收集的停顿类型,而不是区分新生代和老年代的。
FULL GC : 说明这次GC是发生了线程停顿Stop-The-World,如果是System.gc()方法所触发的收集,,那么在这里将显示[Full GC (System)
3. [DefNew,[Tenured,[Perm表示GC发生的区域,这里显示的区域名称和使用的垃圾收集器是密切相关的
1>在Serial收集器中新生代名为Default New Generation ,显示就是DefNew
2>垃圾收集器ParNew中新生代名称就会变为[Parnew,意思为Parallel New Generation
3>如果采用Parrallel Scavenge收集器,那么它配套的新生代名称为PSYongGen
老年代和永久代同理,名称由垃圾收集器决定
4.[DefNew: 102646K->10770K(102976K), 0.0415902 secs] 239776K->153169K(331528K), 0.0416785 secs] [Times: user=0.03 sys=0.02, real=0.04 secs] 就这段而言,
1>方括号内部102646K->10770K(102976K)的含义是GC前改内存区域已使用内存->GC后改内存区域使用容量(该内存区域总容量)
2>方括号外 239776K->153169K(331528K)表示GC前java堆已使用容量->GC后java堆已使用容量(java堆总容量)
3>在往后时间表示该内存区域gc清理用的时间
4>[Times: user=0.03 sys=0.02, real=0.04 secs]
这里面的user、sys和real与linux的time命令所输出的时间含义一致,分别表示用户态消耗的CPU时间、内核态消耗的CPU事件和操作从开始到结束经历的墙钟时间
墙钟时间和CPU时间的区别是,墙钟时间包括各种非运算的等待耗时(i/o,线程阻塞),而CPU时间不包括这些,但是如果是多核,
多线程会叠加这些CPU时间,此时user或sys时间超过real时间完全是正常的
二、获取堆转储的几种方式
Heap Dump 是 Java进程所使用的内存情况在某一时间的一次快照。以文件的形式持久化到磁盘中。
Heap Dump的格式有很多种,而且不同的格式包含的信息也可能不一样。但总的来说,Heap Dump一般都包含了一个堆中的Java Objects, Class等基本信息。同时,当你在执行一个转储操作时,往往会触发一次GC,所以你转储得到的文件里包含的信息通常是有效的内容(包含比较少,或没有垃圾对象了) 。
Heap Dump 包含的信息
所有的对象信息
对象的类信息、字段信息、原生值(int, long等)及引用值
所有的类信息
类加载器、类名、超类及静态字段
垃圾回收的根对象
根对象是指那些可以直接被虚拟机触及的对象
线程栈及局部变量
包含了转储时刻的线程调用栈信息和栈帧中的局部变量信息
Heap Dump 获取方式
1. 使用 jmap 命令生成 dump 文件
jmap -dump:live,format=b,file=d:dumpheap.hprof <pid>
2. 使用 jcmd 命令生成 dump 文件
jcmd <pid> GC.heap_dump d:dumpheap.hprof
3. 使用 JVM 参数获取 dump 文件
1. -XX:+HeapDumpOnOutOfMemoryError
当OutOfMemoryError发生时自动生成 Heap Dump 文件。
这可是一个非常有用的参数,因为当你需要分析Java内存使用情况时,往往是在OOM(OutOfMemoryError)发生时。
2. -XX:+HeapDumpBeforeFullGC
当 JVM 执行 FullGC 前执行 dump。
3. -XX:+HeapDumpAfterFullGC
当 JVM 执行 FullGC 后执行 dump。
4. -XX:+HeapDumpOnCtrlBreak
交互式获取dump。在控制台按下快捷键Ctrl + Break时,JVM就会转存一下堆快照。
5. -XX:HeapDumpPath=d: est.hprof
指定 dump 文件存储路径。
注意:JVM 生成 Heap Dump 的时候,虚拟机是暂停一切服务的。如果是线上系统执行 Heap Dump 时需要注意。
分析 工具:jdk 自带的工具 jvisualvm、Eclipse memory analyzer(jmat)、JProfiler 等。
三、jvisualvm使用
1、点击文件-装入,载入dump下来的文件
2、点击类选项卡
3、根据上图分析实例数或占用大小比例较高的类,右键 “在实例视图中显示”,如
找到可能发生内存泄漏或内存溢出的根对象