理解GC日志

理解GC日志

GC日志对于我们分析Java虚拟机内存问题很有帮助,GC日志格式有很多种,每一种格式都是由垃圾收集器来决定的,但是还是维持了一定的共性。下面就是一部分典型的GC日志(采用Parallel Scavenge和Parallel Old垃圾收集器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{Heap before GC invocations=1 (full 0):
PSYoungGen total 76288K, used 65536K [0x00000007f3300000, 0x00000007f8800000, 0x00000007f8800000)
eden space 65536K, 100% used [0x00000007f3300000,0x00000007f7300000,0x00000007f7300000)
from space 10752K, 0% used [0x00000007f7d80000,0x00000007f7d80000,0x00000007f8800000)
to space 10752K, 0% used [0x00000007f7300000,0x00000007f7300000,0x00000007f7d80000)
ParOldGen total 175104K, used 0K [0x00000007e8800000, 0x00000007f3300000, 0x00000007f3300000)
object space 175104K, 0% used [0x00000007e8800000,0x00000007e8800000,0x00000007f3300000)
Metaspace used 13988K, capacity 14304K, committed 14464K, reserved 1062912K
class space used 1729K, capacity 1822K, committed 1920K, reserved 1048576K
2020-05-30T18:35:21.111-0800: 0.791: [GC (Allocation Failure)
Desired survivor size 11010048 bytes, new threshold 7 (max 15)
[PSYoungGen: 65536K->6797K(76288K)] 65536K->6877K(251392K), 0.0044222 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
Heap after GC invocations=1 (full 0):
PSYoungGen total 76288K, used 6797K [0x00000007f3300000, 0x00000007f8800000, 0x00000007f8800000)
eden space 65536K, 0% used [0x00000007f3300000,0x00000007f3300000,0x00000007f7300000)
from space 10752K, 63% used [0x00000007f7300000,0x00000007f79a3590,0x00000007f7d80000)
to space 10752K, 0% used [0x00000007f7d80000,0x00000007f7d80000,0x00000007f8800000)
ParOldGen total 175104K, used 80K [0x00000007e8800000, 0x00000007f3300000, 0x00000007f3300000)
object space 175104K, 0% used [0x00000007e8800000,0x00000007e8814010,0x00000007f3300000)
Metaspace used 13988K, capacity 14304K, committed 14464K, reserved 1062912K
class space used 1729K, capacity 1822K, committed 1920K, reserved 1048576K
}

可以看到这个日志呈现了一次Young GC过程。0.791代表了GC发生的时间,这个数字的含义是从Java虚拟机启动以来经过的秒数。GC日志开头的 GC代表了GC的类型,这里是Young GC,如果是Full GC,那么这里就是Full GC。接下来的[PSYoungGen]代表GC发生的区域,这里显示的内容跟采用不同的垃圾收集器有关,比如采用了ParNew收集器,那么这里显示的就是ParNew,我们这里采用的是Parallel Scavenge收集器,所以显示的PSYoungGen。后面的 65536K->6797K(76288K)含义是 GC前该内存区域使用容量 -> GC后该内存区域使用容量(该内存区域总容量)65536K->6877K(251392K)这个又代表了 GC前堆已使用容量 -> GC后堆使用容量(堆的总容量)。再往后就是GC所花费的时间。比如说 [Times: user=0.01 sys=0.01, real=0.00 secs] ,分别代表用户态消耗的CPU时间内核态消耗的CPU时间操作从开始到结束所经过的CPU墙钟时间(Wall Clock Time)。CPU时间和墙钟时间的区别是,墙钟时间包括各种非运算的等待耗时,例如等待磁盘I/O、线程阻塞,而CPU时间不包括这些耗时,但当系统有多CPU或者多核的话,多线程操作会叠加这些CPU时间,所以user或sys时间有时候会超过real时间。

PSYoung Gen total代表 新生代的容量,used 代表使用的容量,eden space 代表Eden空间容量,from space 代表From Survivor(S0)空间容量,to space代表To Survivor(S1)空间容量,后面的used代表该内存空间使用率。

ParOldGen total代表老年代的容量,used 代表使用的容量。

Metaspace 解释参考下图。

Metaspace由一个或多个虚拟空间组成,虚拟空间的分配单元是Chunk,其中Chunk使用列表进行维护。

当使用一个classLoader加载一个类时,过程如下:

1
2
3
4
5
6
7
8
1、当前classLoader是否有对应的Chunk且有足够的空间。
2、查找空闲列表中的有没有空闲的Chunk。
3、如果没有,就从当前虚拟空间中分配一个新的Chunk,这个时候会把对应的内存进行Commit,这个动作就是提交。
4、如果当前虚拟空间不足,则预留(reserves)一个新的虚拟空间。

reserved是jvm启动时根据参数和操作系统预留的内存大小。
committed是指那些被commit的Chunk大小之和;
capacity是指那些被实际分配的Chunk大小之和;

因为有GC的存在,有些Chunk的数据可能会被回收,那么这些Chunk属于committed的一部分,但不属于capacity。

另外,这些被分配的Chunk,基本很难被100%用完,存在碎片内存的情况,这些Chunk实际被使用的内存之和即used的大小;

参考

  1. Metaspace Oracle官方文档
  2. GC日志中,Metaspace的committed和reserved含义
  3. JVM源码分析之Metaspace解密
显示评论