最近在看《深入理解java虚拟机》这本书,在看到第3章第二节验证java虚拟机使用的不是引用计算法来判断对象是否被GC回收时。看到了GC日志,不是很清楚是什么意思,在网上搜索资料,自己总结了一下。
如果想要查看java的GC日志,可以通过在java命令加入下列参数进行配置。
-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 日志文件的输出路径
一般加入下列三个参数就好了(博主用的idea,直接在Run/Debug Configurations的VM options配置参数)
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-Xloggc:./gc.log 可自定义日志输出文件
就以我遇到的这个问题为例来分析一下
java代码
public class GCTest {
public Object instance = null;
private static final int _1MB = 1024*1024;
private byte[] bigSize = new byte[2*_1MB];
public static void main(String[] args){
GCTest a = new GCTest();
GCTest b = new GCTest();
a.instance = b;
b.instance = a;
a = null;
b = null;
System.gc();// 这里进行了一次垃圾回收
}
}
GC日志(博主用的是java1.8_131,各个版本的javaGC日志有略微区别):
Java HotSpot(TM) 64-Bit Server VM (25.131-b11) for windows-amd64 JRE (1.8.0_131-b11), built on Mar 15 2017 01:23:53 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16682356k(6409344k free), swap 33362812k(21468660k free)
CommandLine flags: -XX:InitialHeapSize=266917696 -XX:MaxHeapSize=4270683136 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
2019-11-18T16:34:17.811+0800: 0.314: [GC (System.gc()) [PSYoungGen: 12063K->1499K(76288K)] 12063K->1507K(251392K), 0.0092309 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
2019-11-18T16:34:17.820+0800: 0.323: [Full GC (System.gc()) [PSYoungGen: 1499K->0K(76288K)] [ParOldGen: 8K->1421K(175104K)] 1507K->1421K(251392K), [Metaspace: 3521K->3521K(1056768K)], 0.0083844 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 76288K, used 1966K [0x000000076b200000, 0x0000000770700000, 0x00000007c0000000)
eden space 65536K, 3% used [0x000000076b200000,0x000000076b3eb9e0,0x000000076f200000)
from space 10752K, 0% used [0x000000076f200000,0x000000076f200000,0x000000076fc80000)
to space 10752K, 0% used [0x000000076fc80000,0x000000076fc80000,0x0000000770700000)
ParOldGen total 175104K, used 1421K [0x00000006c1600000, 0x00000006cc100000, 0x000000076b200000)
object space 175104K, 0% used [0x00000006c1600000,0x00000006c1763580,0x00000006cc100000)
Metaspace used 3534K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 389K, capacity 392K, committed 512K, reserved 1048576K
重点关注四、五行,先看第四行
2019-11-18T16:34:17.811+0800: 0.314:时间;
GC和Full GC :代表垃圾收集的停顿类型,Full GC代表发生了STW(Stop The World:停止当前所有java线程执行GC),如果是调用了System.gc()方法触发的收集,后面会带(System.gc());
[PSYoungGen: 12063K->1499K(76288K)]:[GC类型(此处是Parallel Scavenge垃圾收集器): GC前新生代内存占用大小->GC后新生代内存占用大小(新生代总大小)];
12063K->1507K(251392K):Parallel Scavenge垃圾收集器GC前java堆内存占用大小->GC后java堆内存占用大小(java堆总大小);
0.0092309 secs:YoungGenGC耗时;
[Times: user=0.05 sys=0.00, real=0.01 secs] :分别表示用户消耗CPU的时间、内核消耗CPU的时间和GC从操作开始到结束所经过的墙上挂钟时间(当系统有多个CPU或者多核时,usr或sys时间超过real时间完全正常,此处是来自《深入理解java虚拟机》第二版3.5.8节,一开始我看第一遍时也不太理解,后来发现usr时间应该是每个用户消耗每个cpu时间的总和,sys也是一样,而real指的是现实中这整个GC过程耗费的时间);
[PSYoungGen: 1499K->0K(76288K)]:Parallel Scavenge垃圾收集器GC前新生代内存占用大小->GC后新生代内存占用大小(新生代总大小);
[ParOldGen: 8K->1421K(175104K)]:Parallel Old垃圾收集器GC前老年代内存占用大小->GC后老年代内存占用大小(老年代总大小);
[Metaspace: 3521K->3521K(1056768K)]:GC前元空间(jdk1.8去除了永久代,变成了元空间)内存占用大小->GC后元空间内存占用大小(元空间总大小);