关于垃圾收集器ZGC的扩展

一、ZGC背景

Java项目在JVM进行垃圾回收时有个很大的问题,即STW(Stop The World)。
当JVM进行垃圾回收时,会暂停所有的应用程序线程,即业务线程,因而导致业务系统的暂停。
在这里插入图片描述

STW引发的问题

1、手机系统(Android) 显示卡顿

Google 主导的 Android 系统需要解决的一大问题就是显示卡顿问题,通过对 GC 算法的不断演进,停顿时间控制在几个ms级别。
所以这也是Android与苹果IOS系统竞争的一大利器。

2、证券交易系统实时性要求

证券交易系统主要就是买入与卖出,现在都是使用系统完成自动下单。如果用Java系统来做,遇到了STW,假设STW的时间是3秒,此时刚收到市场行情是比较低而进行买入,但是因为STW卡顿了3秒,3秒后的市场行情可能完全不同。所以若使用Java来做证券系统,一定是要求STW时间越短越好!

3、大数据平台(Hadoop集群性能)

例如58同城、腾讯、阿里等公司的大数据系统,单集群5000+的Hadoop集群,日万亿级实时数据分发,若遇到STW也是不行的。

垃圾回收器的发展

为了满足不同的业务需求,Java 的 GC 算法也在不停迭代,对于特定的应用,选择其最适合的 GC 算法,才能更高效的帮助业务实现其业务目标。对于这些延迟敏感的应用来说,GC 停顿已经成为阻碍 Java 广泛应用的一大顽疾,需要更适合的 GC 算法以满足这些业务的需求。
近些年来,服务器的性能越来越强劲,各种应用可使用的堆内存也越来越大,常见的堆大小从10G到百G级别,部分机型甚至可以到达TB级别,在这类大堆应用上,传统的GC,如CMS、G1的停顿时间也跟随着堆大小的增长而同步增加,即堆大小指数级增长时,停顿时间也会指数级增长。特别是当触发Full GC时,停顿可达分钟级别(百GB级别的堆)。当业务应用需要提供高服务级别协议(Service Level Agreement,SLA),例如 99.99% 的响应时间不能超过 100ms,此时CMS、G1等就无法满足业务的需求。
为满足当前应用对于超低停顿、并应对大堆和超大堆带来的挑战,伴随着 2018 年发布的 JDK 11,A Scalable Low-Latency Garbage Collector - ZGC 应运而生。
在这里插入图片描述

单线程:Serial、SerialOld
多线程:ParallelScavenge、ParallelOld
多线程+并发:CMS、G1、Shenandoah、ZGC(STW控制在1ms)

二、ZGC介绍

ZGC(The Z Garbage Collector)是JDK 11中推出的一款追求极致低延迟的垃圾收集器。
设计目标:

  • 停顿时间不超过10ms(JDK16已经达到不超过1ms)
  • 停顿时间不会随着堆的大小,或者活跃对象的大小而增加
  • 支持8MB~4TB级别的堆(JDK15后已经可以支持16TB)

若使用ZGC来做Java项目,像对STW敏感的证券系统,游戏的系统便都可以去用Java来做(以前都是C或者C++的市场),所以ZGC的出现就是为了抢占其他语言的市场!

三、ZGC中的内存布局(分页模型)

为了细粒度地控制内存的分配,和G1一样,ZGC将内存划分成小的分区,在ZGC中称为页面(page)。

ZGC中没有分代的概念(新生代、老年代)。

ZGC支持3种页面,分别为小页面、中页面和大页面

其中小页面指的是2MB的页面空间,中页面指32MB的页面空间,大页面指受操作系统控制的大页面空间。
在这里插入图片描述

当对象大小小于等于256KB时,对象分配在小页面。
当对象大小在256KB和4M之间,对象分配在中页面。
当对象大于4M,对象分配在大页面。

ZGC对于不同页面回收的策略也不同。简单来说:小页面优先回收,中页面和大页面则尽量不回收

设计理由

标准大页(huge page)是Linux Kernel 2.6引入的,目的是通过使用大页内存来取代传统的4KB内存页面,以适应越来越大的系统内存,让操作系统可以支持现代硬件架构的大页面容量功能。

Huge pages有两种格式大小:2MB(默认)和1GB。2MB页块大小适合用于GB大小的内存,1GB页块大小适合用于TB级别的内存。

因此ZGC的设置也是为了适应现代硬件架构的发展,提升性能。

ZGC支持NUMA

在过去,对于X86架构的计算机,内存控制器还没有整合进CPU,所有对内存的访问都需要通过北桥芯片来完成。X86系统中的所有内存都可以通过CPU进行同等访问。任何CPU访问任何内存的速度是一致的,不必考虑不同内存地址之间的差异,即统一内存访问(Uniform Memory Access,UMA)。

UMA系统的架构示意:
在这里插入图片描述
在UMA中,各处理器与内存单元通过互联总线进行连接,各个CPU之间没有主从关系。之后的X86平台经历了一场从“拼频率”到“拼核心数”的转变,越来越多的核心被尽可能地塞进了同一块芯片上,各个核心对于内存带宽的争抢访问成为瓶颈。

为了应对此问题便设计出非统一内存访问(Non-Uniform Memory Access,NUMA),将CPU和内存集成在一个单元上(称为Socket)。

在NUMA下,CPU访问本地存储器的速度显然比访问非本地存储器快一些。

NUMA处理器架构示意:
在这里插入图片描述
ZGC支持NUMA,在进行小页面分配时会优先从本地内存分配,当不能分配时才会从远端的内存分配。对于中页面和大页面的分配,ZGC并没有要求从本地内存分配,而是直接交给操作系统,由操作系统找到一块能满足ZGC页面的空间。

ZGC如此设计的目的在于,对于小页面,存放均为小对象,从本地内存分配速度很快,且不会造成内存使用的不均衡;而中页面和大页面由于需要的空间大,若也优先从本地内存分配,极易造成内存使用不均衡,反而影响性能。

四、ZGC的核心概念

指针着色技术(Colored Pointers)

颜色指针为ZGC的核心概念,它会在指针中借4位用来记录,因此它必须要求在64位的机器上才可以工作,也由于是64位的指针,因此也不支持压缩指针。
指针中低42位表示使用中的堆空间,高18位为预留空间,而中间的4位则被ZGC所使用。
ZGC借用此4位来做GC相关的事情(例如快速实现垃圾回收中的并发标记、转移和重定位等)。
在这里插入图片描述

五、ZGC流程

一次ZGC流程:

在这里插入图片描述

1、标记阶段:标识垃圾(根据根可达分析算法)

根可达分析算法

用来判定对象是否存活。

将一系列作为“GC Roots”的对象为起始点,从这些节点开始向下扫描,所形成的路径称为引用链(Reference Chain),在引用链上的对象则会被标记为非垃圾对象。

即当一个对象没有在GC Roots的引用链中,则说明此对象是无用的。

还有另一种算法为程序计数法,根据对象被其他对象引用的数量来判定,若引用计数为0则说明没有被任何对象引用,判定为垃圾对象。
但无法解决对象间循环引用的问题,即两个垃圾对象互相引用,但实际上程序已不再使用,而根可达分析算法可以解决这个问题,只要对象不被GC Roots所引用即为垃圾对象。
根可达分析算法的标记效率也比程序计数法高。

GC Roots的对象(以下4种为主要)

  1. 虚拟机栈(栈帧中的本地变量表):

    各个线程调用方法堆栈中使用到的参数、局部变量、临时变量等。

  2. 方法区中类静态变量:

    Java类的引用类型静态变量。

  3. 方法区中常量:

    例如字符串常量池里的引用。

  4. 本地方法栈中JNI指针:

    native修饰的方法。

GC Roots的s表示Set,即为GC根的集合

ZGC中垃圾收集算法流程

根据根可达分析算法提高收集效率。

  1. 标记(mark):

    从GC Roots出发,标记存活对象,此时内存中存在存活对象和死亡对象。

  2. 转移(relocate):

    将存活对象转移(复制)到新的内存上,原来的内存空间可以回收。

  3. 重定位(remap):

    由于对象的内存地址发生改变,因此要将所有指向旧地址的指针变更为指向新地址。

2、转移阶段:对象复制或移动

若是同一个页面,等同于标记整理算法:
在这里插入图片描述
若是不同页面,等同于复制算法:
在这里插入图片描述

3、ZGC基于指针着色的并发标记算法

0.初始阶段

在ZGC初始化后,此时地址视图为Remapped,程序正常运行,在内存中分配对象,满足一定条件后垃圾收集启动。

1.初始标记

此阶段会STW,初始标记只需要扫描所有GC Roots,其处理时间和GC Roots的数量成正比,停顿时间不会随着堆的大小或者活跃对象的大小而增加。

相邻的两次GC会有时间间隔,M0和M1切换使用

第一次GC时,M0标记所有GC Roots的指针
第二次GC时,M1标记所有GC Roots的指针

2.并发标记

此阶段不会STW,扫描剩余的所有对象,处理时间比较长,所以使用并发,业务线程与GC线程会同时运行,也因此会出现漏标。

第一次GC时,M0标记GC Roots下的引用链指针
第二次GC时,M1标记GC Roots下的引用链指针

3.再标记

此阶段会STW,主要处理漏标对象,通过SATB算法解决(G1中的解决漏标的方案)。

第一次GC时,M0标记漏标对象指针
第二次GC时,M1标记漏标对象指针

4、ZGC基于指针着色的并发转移算法

ZGC的转移阶段

  1. 并发转移准备:

    分析哪个GC分页垃圾多,若有分页全是垃圾,会直接清除此分页的垃圾,不会STW

  2. 初始转移:

    转移初始标记的存活对象(即被M0标记的指针引用对象复制到新内存),同时做对象重定位(即将指针指向从旧内存地址改成新内存地址),会STW

此时被转移的对象会被Remapped标记,刚new出来的对象也会被Remapped标记

  1. 并发转移:

    对转移并发标记的存活对象做转移,不会STW

并发转移

根据转发表(类似于HashMap)来确定最新引用地址,表里记录指针及其新旧内存地址的映射。

对象转移和插转发表做原子操作。

5、ZGC基于指针着色的重定位算法

并发标记对象的重定位:
第二次GC中的并发标记阶段,除了标记GC Roots的引用链指针外,同时还会做上次并发标记对象的重定位,做好重定位之后便将转发表利对应的记录删除。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值