今天我们分享的内容是:新一代 Java垃圾回收神器:ZGC。
ZGC(The Z Garbage Collector),是一种可扩展的低延迟垃圾收集器,主要是用来处理超大内存(TB级别)的垃圾回收。
ZGC 最初是 JDK 11 以一项实验性功能引入的,经过几个版本的迭代,最终在 JDK 15中被宣布为 Production Ready。
ZGC的中的"Z"代表什么含义?官方解释如下:
大概意思为:Z它不代表任何东西,ZGC只是一个名字。它最初受到 ZFS(文件系统)的启发或致敬,ZFS(文件系统)在首次问世时在许多方面都是革命性的。最初,ZFS 是“Zettabyte File System”的首字母缩写词,但这个意思被废弃了,后来据说它不代表任何东西。这只是一个名字。有关详细信息,请参阅Jeff Bonwick 的博客。
下图为 Oracle官方对 ZGC停顿时间的描述:
2018年,Oracle官方描述的 ZGC目标为:
2022年11月,Oracle官方描述的 ZGC的目标为:
通过官方对 ZGC目标的描述也可以看出,在几年的时间内,ZGC垃圾回收器的最大停顿时间已从 10ms 降低到 亚毫秒 级别,性能有了质的飞越。
2018年,官方描述的 ZGC的核心技术点为:
2022年,官方描述的 ZGC的核心技术点为:
比较 2018年 和 2022年官方对 ZGC核心技术的描述可以发现,官方把 Single generation 这一点去掉了,熟悉 G1的小伙伴应该知道,G1是 Region-based类型,每个 Region在同一时刻只能属于一种分代,但是,一个Region可以在多个分代之间动态切换,因此,ZGC 从 最初的不分代发展成和 G1一样,也有分代。
Region-based:基于区域。
ZGC 和 G1等垃圾回收器一样,也会将堆划分成很多的小分区,整个堆内存分区如下图:
ZGC的 Region 有小、中、大三种类型:
Compacting:整理内存。
因为 ZGC回收器和 CMS、G1等垃圾回收器一样,使用了"标记-复制算法",该算法会产生内存碎片,因此需要进行内存整理操作,清除内存碎片。
NUMA,Non-Uniform Memory Access(非一致内存访问)。
最初的计算机是单核处理器,一个 CPU访问一块内存,但是随着网络的快速发展,单核远不能满足实际需求,因此采用多核处理器技术,多 CPU需要访问同一个内存,因为任一 CPU对同一内存的访问速度是一致的,所以也称作一致内存访问(Uniform Memory Access, UMA),再随着网络的发展,单内存无法满足需求,因此就诞生了多内存,把 CPU和内存集成到同一个单元,这样 CPU就会访问离它最近的内存,提升读写效率,这种方式就是非一致内存访问。
NUMA和UMA比较如下图:
NUMA 架构在中大型系统上非常流行,是一种高性能的解决方案,ZGC就充分利用 NUMA架构的特征。
Colored pointers:染色指针,一种将数据存放在指针里的技术,JVM是通过染色指针来标识某个对象是否需要被GC。
像 CMS,G1这些垃圾收集器的 GC信息都保存在对象头中,而 ZGC的 GC信息保存在指针中,每个对象有一个 64位指针,参考 JDK zGlobals_x86.cpp 源码,ZGC 地址空间和指针结构有如下 3种布局:
布局1
抽象成结构图如下:
布局2
抽象成结构图如下:
布局3
抽象成结构图如下:
通过上面的 3种布局,可以发现一个共性:分配 4-bits来分别存放 Marked0,Marked1,Remapped,Finalizable 染色标记位,4种染色标记位说明如下:
上述 Marked0,Marked1,Remapped染色标记位其实代表一种地址视图,当应用程序创建对象时,首先在堆空间申请一个虚拟地址,ZGC同时会为该对象在 Marked0、Marked1和 Remapped地址空间分别申请一个虚拟地址,三个虚拟地址指向同一个物理地址,并且在同一时间,三个虚拟地址有且只有一个空间有效,整个视图映射关系如下图:
ZGC之所以设置三个虚拟地址空间,目的是使用"虚拟空间换时间"的思想,从而降低GC停顿时间。三个空间的切换是由垃圾回收的不同阶段触发的,通过限定三个空间在同一时间点有且仅有一个空间有效高效的完成GC过程的并发操作。
Load barriers:读屏障,是指 JIT( just-in-time compilation 即时编译器,JVM)向应用代码注入一小段代码,当从堆中读取对象引用时,就会执行这段代码,官方说明如下:
读屏障示例:
java复制代码String n = person.name; // 从堆中读取引用,需要加入屏障
if (n & bad_bit_mask) {
slow_path(register_for(n), address_of(person.name));
}
String p = n ; // 无需屏障,不是从堆中读取引用
n.isEmpty() ; // 无需屏障,不是从堆中读取引用
int age = person.age; // 无需屏障,不是对象引用
在读屏障示例中,JVM 注入了如下的一段读屏障代码:
text复制代码if (n & bad_bit_mask) {
slow_path(register_for(n), address_of(person.name));
}
对应的字节码如下:
java复制代码mov 0x10(%rax), %rbx // String n = person.name;
test %rbx, 0x20(%r15) // Bad color?
jnz slow_path // Yes -> Enter slow path and
// mark/relocate/remap, adjust
// 0x10(%rax) and %rbx
假如 person 对象发生移动,因此 n 和 person.name 的地址都会发生变化,当使用 n 前,需要判断 n 的染色指针是否为 good,如果为 bad color,可以得知 n 的引用地址被修改过,因此需要修正 n 和 person.name的地址,整个过程如下图:
上图过程也称为"自愈",即当对象地址发生转移时,通过读屏障操作,不仅赋值的引用更改为最新值,自身引用也被修正了,整个过程看起来像自愈。
这里对"转移"做个解释:ZGC是按照 Page内存页进行垃圾回收的,也就是说当对象所在的页面需要回收时,页面里还存活的对象需要被转移到其他页。
Concurrent:并发,指 GC线程和应用线程是并发执行。
和 MCS、G1等垃圾回收器一样,ZGC也采用了标记-复制算法,不过,ZGC对标记-复制算法做了很大的改进,ZGC垃圾回收周期和视图切换可以抽象成下图:
下图以 obj1,obj2,obj3 三个对象为案例对垃圾回收和视图切换进行了演示:
ZGC垃圾回收全过程包含:初始标记、并发标记、再标记、并发转移准备、初始转移、并发转移 6个阶段,如下图:
在 ZGC 全过程中,会出现 3个 STW(Stop The World):初始标记,再标记,初始转移。尽管这 3个阶段会 STW,但是 ZGC对 STW的暂停时间是有严格要求,一般是 1ms 甚至更低,下面将分别介绍各个阶段:
为什么需要 Marked0 和 Marked1 两个标识?
简单地说是为了区别上一次标记和当前标记,因为每个 GC周期开始时,会交换使用的 Marked标记位,使上次 GC周期中修正的已标记状态失效,所有引用都变成未标记。
比如:GC周期1 使用Marked0,则 GC周期结束后,所有引用 Marked0标记都会成为 01(二进制的01 = 0);GC周期2使用 Marked1,类同于周期1,所有的 Marked标记都会成为 10(二进制的10 = 1)。
为什么会有 3种内存布局?
这主要还是和 ZGC的目标(支持 8MB-16TB 的堆)相匹配,因为 ZGC需要支持最大 16TB Java堆内存的垃圾回收,所以就需要用不同 bit位数来表示,因此就出现了3种布局。
使用多重视图和染色指针的优点
像 CMS,G1等垃圾回收器,GC信息是存放在对象头中,因此每次修改对象头信息时都需要先访问内存,然后操作,而 ZGC是把 GC信息存放在指针的有色标记位上,修改GC信息时,无需任何对象访问,只需要设置地址中对应的标志位即可,因此可以加快标记和转移的速度,这也是 ZGC在标记和转移阶段速度更快的原因。
ZGC 支持哪些平台?
下面是官方文档列举的所有支持平台:目前,ZGC目前支持了大多数的操作系统,并且是 64位系统。
shell复制代码-XX:+UseZGC -Xmx -Xlog:gc
# 或者
-XX:+UseZGC -Xmx -Xlog:gc*
ZGC 最初是作为 JDK 11 中的一项实验性功能引入的,并在 JDK 15 中被宣布为 Production Ready
从 不支持类卸载 到 支持类卸载
从仅支持个别系统到支持多个平台
从不支持指针压缩,到支持压缩类指针
JDK 16 支持并发线程堆栈扫描
......
ZGC 随着JDK发展的更改日志如下:
通过 ZGC的发展可以看出:GC垃圾回收器已经越来越智能化,GC会自适应各种情况自动优化。
页面更新:2024-05-22
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号