1.怎么查看日志
首先在eclipse中准备一个Gcdemo.java
package leetcode;
public class Gcdemo {
public static void main(String[] args) {
int _1m = 1024 * 1024;
byte[] data = new byte[_1m];
// 将data置为null即让它成为垃圾
data = null;
// 通知垃圾回收器回收垃圾
System.gc();
}
}
按如下图运行这个demo 右键- Run AS - Run Configurations
输入如下图参数-XX:+PrintGCDetails
得到如图内容
当然也可以通过cmd的形式。
在工程的bin层打开cmd 输入命令:java -XX:+PrintGCDetails leetcode.Gcdemo
2.GC日志的参数
首先了解关于输出GC日志的参数有以下几种
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
3.分析日志
[GC (System.gc()) [PSYoungGen: 2908K->680K(54784K)] 2908K->688K(180736K), 0.0314018 secs] [Times: user=0.00 sys=0.00, real=0.07 secs]
[Full GC (System.gc()) [PSYoungGen: 680K->0K(54784K)] [ParOldGen: 8K->530K(125952K)] 688K->530K(180736K), [Metaspace: 2656K->2656K(1056768K)], 0.0115257 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 54784K, used 471K [0x0000000782f00000, 0x0000000786c00000, 0x00000007c0000000)
eden space 47104K, 1% used [0x0000000782f00000,0x0000000782f75da0,0x0000000785d00000)
from space 7680K, 0% used [0x0000000785d00000,0x0000000785d00000,0x0000000786480000)
to space 7680K, 0% used [0x0000000786480000,0x0000000786480000,0x0000000786c00000)
ParOldGen total 125952K, used 530K [0x0000000708c00000, 0x0000000710700000, 0x0000000782f00000)
object space 125952K, 0% used [0x0000000708c00000,0x0000000708c84848,0x0000000710700000)
Metaspace used 2663K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
①GC日志开头的[GC和[Full GC说明了这次垃圾收集的类型.
Partial GC(局部 GC): 并不收集整个 GC 堆的模式
- Young GC: 只收集young gen的GC,Young GC还有种说法就叫做 "Minor GC"
- Old GC: 只收集old gen的GC。只有垃圾收集器CMS的concurrent collection 是这个模式
- Mixed GC: 收集整个young gen 以及部分old gen的GC。只有垃圾收集器 G1有这个模式
Full GC 就是收集整个堆,包括新生代,老年代,永久代(在JDK 1.8及以后,永久代会被移除,换为metaspace(元空间))等收集所有部分的模式。
针对不同的垃圾收集器,Full GC的触发条件可能不都一样。按HotSpot VM的serial GC的实现来看,触发条件是:
1.当准备要触发一次 young GC时,如果发现统计数据说之前 young GC的平均晋升大小比目前的 old gen剩余的空间大,则不会触发young GC而是转为触发 full GC
(因为HotSpot VM的GC里,除了垃圾回收器 CMS的concurrent collection 之外,其他能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以不需要事先准备一次单独的young GC)
2.如果有永久代(perm gen),要在永久代分配空间但已经没有足够空间时,也要触发一次 full GC
3.执行System.gc(),heap dump带GC,其默认都是触发 full GC.
②[PSYoungGen和[ParOldGen是指GC发生的区域,分别代表使用Parallel Scavenge垃圾收集器的新生代和使用Parallel old垃圾收集器的老生代。为什么是这两个垃圾收集器组合呢?因为我的jvm开启的模式是Server,而Server模式的默认垃圾收集器组合便是这个,在命令行输入java -version就可以看到自己的jvm默认开启模式。还有一种是client模式,默认组合是Serial收集器和Serial Old收集器组合。
③在方括号中PSYoungGen:后面的2908K->680K(54784K)代表的是GC前该内存区域已使用的容量->GC后该内存区域已使用的容量(该内存区域总容量)
④在方括号之外的2908K->688K(180736K)代表的是GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量)
⑤再往后的0.0314018 secs代表该内存区域GC所占用的时间,单位是秒。
⑥再后面的[Times: user=0.00 sys=0.00, real=0.07 secs],user代表进程在用户态消耗的CPU时间,sys代表代表进程在内核态消耗的CPU时间、real代表程序从开始到结束所用的时钟时间。这个时间包括其他进程使用的时间片和进程阻塞的时间(比如等待 I/O 完成)
下面是堆详细信息的日志
Heap
PSYoungGen total 54784K, used 471K [0x0000000782f00000, 0x0000000786c00000, 0x00000007c0000000)
eden space 47104K, 1% used [0x0000000782f00000,0x0000000782f75da0,0x0000000785d00000)
from space 7680K, 0% used [0x0000000785d00000,0x0000000785d00000,0x0000000786480000)
to space 7680K, 0% used [0x0000000786480000,0x0000000786480000,0x0000000786c00000)
ParOldGen total 125952K, used 530K [0x0000000708c00000, 0x0000000710700000, 0x0000000782f00000)
object space 125952K, 0% used [0x0000000708c00000,0x0000000708c84848,0x0000000710700000)
Metaspace used 2663K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 288K, capacity 386K, committed 512K, reserved 1048576K
先了解下Java memory划分:
Java memory主要分heap memory和non-heap memory,如下图:
⑦第一行为新生代的大小,大小为54784K。而新生代又分为三个区域分别叫Eden,和俩个Survivor spaces(from和to)。Eden用来存放新的对象,Survivor spaces用于 新对象 升级到Tenured area时的 拷贝。默认的,Edem : from : to = 8 : 1 : 1( 可以通过参数–XX:SurvivorRatio来设定 ),即:Eden = 8/10 的新生代空间大小,from = to = 1/10的新生代空间大小。
⑧默认的,新生代 ( Young) 与老年代 ( Old ) 的比例的值为 1:2( 该值可以通过参数–XX:NewRatio来指定 ),即:新生代 ( Young) = 1/3的堆空间大小。老年代 ( Old) = 2/3 的堆空间大小。其中,新生代 ( Young) 被细分为 Eden 和 两个 Survivor区域,这两个 Survivor区域分别被命名为 from 和 to,以示区分。
⑨ParOldGen为老年代,大小为125952K,大约为PSYoungGen内存大小的2倍。 从JDK8开始,永久代(PermGen)的概念被废弃掉了,取而代之的是一个称为Metaspace(元空间)的存储空间。Metaspace与PermGen之间最大的区别在于:Metaspace并不在虚拟机中,而是使用本地内存。