Java最新前沿技术:ZGC垃圾收集器

2023-11-03 17:30

本文主要是介绍Java最新前沿技术:ZGC垃圾收集器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


南京 | 摄影©万里

ZGC介绍

ZGC(The Z Garbage Collector)是JDK 11中推出的一款追求极致低延迟的实验性质的垃圾收集器,它曾经设计目标包括:

  • 停顿时间不超过10ms;

  • 停顿时间不会随着堆的大小,或者活跃对象的大小而增加;

  • 支持8MB~4TB级别的堆,未来支持16TB。

基于最新的JDK15来看,“停顿时间不超过10ms”和“支持16TB的堆”这两个目标已经实现,并且官方明确指出JDK15中的ZGC不再是实验性质的垃圾收集器,且建议投入生产了。

ZGC以追求低停顿为主要目标,STW的时候能控制在10ms以内。本文会从ZGC的设计思路出发,讲清楚为何ZGC能在低延时场景中的应用中有着如此卓越的表现。

多重映射

ZGC参照操作系统中的虚拟地址和物理地址,设计了一套内存和地址的多重映射关系。

为了能更好的理解ZGC的多重映射,我们先看一下这个例子:

你在你爸爸妈妈眼中是儿子,在你女朋友眼中是男朋友。在全世界人面前就是最帅的人。你还有一个名字,但名字也只是你的一个代号,并不是你本人。

将这个关系画一张映射图表示:

假如你的名字是全世界唯一的,通过“你的名字”、“你爸爸的儿子”、“你女朋友的男朋友”,“世界上最帅的人”最后定位到的都是你本人。

现在我们再来看看ZGC的内存映射。

ZGC为了能高效、灵活地管理内存,实现了两级内存管理:虚拟内存和物理内存,并且实现了物理内存和虚拟内存的映射关系。

当应用程序创建对象时,首先在堆空间申请一个虚拟地址,ZGC同时会为该对象在Marked0、Marked1和Remapped三个视图空间分别申请一个虚拟地址,且这三个虚拟地址对应同一个物理地址。

图中的Marked0、Marked1和Remapped三个视图是什么意思呢?

这是ZGC的三个视图空间,在ZGC中这三个空间在同一时间点有且仅有一个空间有效。

对照上面的例子,这三个视图分别对应的就是"你爸爸眼中",“你女朋友的眼中”,“全世界人眼中”。

而三个视图里面的地址,都是虚拟地址。对应的是“你爸爸眼中的儿子”,“你女朋友眼中的男朋友”......

最后,这些虚地址都能映射到同一个物理地址,这个物理地址对应上面例子中的“你本人”。

用一段简单的Java代码表示这种关系:

ZGC为什么这么设计呢?这就是ZGC的高明之处,利用虚拟空间换时间,这三个空间的切换是由垃圾回收的不同阶段触发的,通过限定三个空间在同一时间点有且仅有一个空间有效,高效的完成了GC过程的并发操作,具体实现会在后面讲ZGC并发处理算法的部分详细描述。

染色指针

染色指针是一种将信息存储在指针中的技术。

我们都知道,之前的垃圾收集器都是把GC信息(标记信息、GC分代年龄..)存在对象头的Mark Word里。举个例子:

如果某个人是个垃圾人,就在这个人的头上盖一个“垃圾”的章;如果这个人不是垃圾了,就把这个人头上的“垃圾”印章洗掉。

而ZGC是这样做的:

如果某个人是垃圾人。就在这个人的身份证信息里面标注这个人是个垃圾,以后不管这个人在哪刷身份证,别人都知道他是个垃圾人了。也许哪一天,这个人醒悟了不再是垃圾人了,就把这个人身份证里面的“垃圾”标志去掉。

在这例子中,“这个人”就是一个对象,而“身份证”就是指向这个对象的指针。这种指针有一个高大上的名字——染色指针(Colored Pointer)。

在64位的机器中,对象指针是64位的。

  • ZGC使用64位地址空间的第0~43位存储对象地址,2^44 = 16TB,所以ZGC最大支持16TB的堆。

  • 而第44~47位作为颜色标志位,Marked0、Marked1和Remapped分别对应三个视图空间。‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

  • 第48~63位固定为0暂时没有使用。

读屏障

读屏障是JVM向应用代码插入一小段代码的技术。

当应用线程从堆中读取对象引用时,就会执行这段代码。千万不要把这个读屏障和Java内存模型里面的读屏障搞混了,两者根本不是同一个东西,ZGC中的读屏障更像是一种AOP技术,在字节码层面或者编译代码层面给读操作增加一个额外的处理。

读屏障实例:

ZGC中读屏障的代码作用:

GC线程和应用线程是并发执行的,所以存在应用线程去A对象内部的引用所指向的对象B的时候,这个对象B正在被GC线程移动或者其他操作,加上读屏障之后,应用线程会去探测对象B是否被GC线程操作,然后等待操作完成再读取对象,确保数据的准确性。

具体的探测和操作步骤如下:

这样会影响程序的性能吗?

会。据测试,最多百分之4的性能损耗。但这是ZGC并发转移的基础,为了降低STW,设计者认为这点牺牲是可接受的。

ZGC并发处理算法

ZGC并发处理算法利用全局空间视图的切换和对象地址视图的切换,结合SATB算法实现了高效的并发。

以上所有的铺垫,都是为了讲清楚ZGC的并发处理算法,在一些博文上,都说染色指针和读屏障是ZGC的核心,但都没有讲清楚两者是如何在算法里面被利用的,我认为,ZGC的并发处理算法才是ZGC的核心,染色指针和读屏障只不过是为算法服务而已。

ZGC的并发处理算法三个阶段的全局视图切换如下:

  • 初始化阶段:ZGC初始化之后,整个内存空间的地址视图被设置为Remapped

  • 标记阶段:当进入标记阶段时的视图转变为Marked0(以下皆简称M0)或者Marked1(以下皆简称M1)

  • 转移阶段:从标记阶段结束进入转移阶段时的视图再次设置为Remapped

标记阶段

标记阶段全局视图切换到M0视图。因为应用程序和标记线程并发执行,那么对象的访问可能来自标记线程和应用程序线程。

在标记阶段结束之后,对象的地址视图要么是M0,要么是Remapped。

  • 如果对象的地址视图是M0,说明对象是活跃的;

  • 如果对象的地址视图是Remapped,说明对象是不活跃的,即对象所使用的内存可以被回收。

当标记阶段结束后,ZGC会把所有活跃对象的地址存到对象活跃信息表,活跃对象的地址视图都是M0。

转移阶段

转移阶段切换到Remapped视图。因为应用程序和转移线程也是并发执行,那么对象的访问可能来自转移线程和应用程序线程。

至此,ZGC的一个垃圾回收周期中,并发标记和并发转移就结束了。


为何要设计M0和M1

我们提到在标记阶段存在两个地址视图M0和M1,上面的算法过程显示只用到了一个地址视图,为什么设计成两个?简单地说是为了区别前一次标记和当前标记。

ZGC是按照页面进行部分内存垃圾回收的,也就是说当对象所在的页面需要回收时,页面里面的对象需要被转移,如果页面不需要转移,页面里面的对象也就不需要转移。

如图,这个对象在第二次GC周期开始的时候,地址视图还是M0。如果第二次GC的标记阶段还切到M0视图的话,就不能区分出对象是活跃的,还是上一次垃圾回收标记过的。这个时候,第二次GC周期的标记阶段切到M1视图的话就可以区分了,此时这3个地址视图代表的含义是:

  • M1:本次垃圾回收中识别的活跃对象。

  • M0:前一次垃圾回收的标记阶段被标记过的活跃对象,对象在转移阶段未被转移,但是在本次垃圾回收中被识别为不活跃对象。

  • Remapped:前一次垃圾回收的转移阶段发生转移的对象或者是被应用程序线程访问的对象,但是在本次垃圾回收中被识别为不活跃对象。

现在,我们可以回答“使用地址视图和染色指针有什么好处”这个问题了。

使用地址视图和染色指针可以加快标记和转移的速度。

以前的垃圾回收器通过修改对象头的标记位来标记GC信息,这是有内存存取访问的,而ZGC通过地址视图和染色指针技术,无需任何对象访问,只需要设置地址中对应的标志位即可。这就是ZGC在标记和转移阶段速度更快的原因。

当GC信息不再存储在对象头上时而存在引用指针上时,当确定一个对象已经无用的时候,可以立即重用对应的内存空间,这是把GC信息放到对象头所做不到的。

ZGC垃圾回收周期

ZGC采用的是标记-复制算法,标记、转移和重定位阶段几乎都是并发的,ZGC垃圾回收周期如下图所示:

ZGC只有三个STW阶段:初始标记再标记初始转移

其中,初始标记和初始转移分别都只需要扫描所有GC Roots,其处理时间和GC Roots的数量成正比,一般情况耗时非常短;

再标记阶段STW时间很短,最多1ms,超过1ms则再次进入并发标记阶段。即ZGC几乎所有暂停都只依赖于GC Roots集合大小,停顿时间不会随着堆的大小或者活跃对象的大小而增加。

ZGC的发展

ZGC诞生于JDK11,经过不断的完善,JDK15中的ZGC已经不再是实验性质的了。

从只支持Linux/x64,到现在支持多平台;从不支持指针压缩,到支持压缩类指针.....ZGC迭代的速度非常快。

在即将发布的JDK16中,ZGC将支持并发线程栈扫描(Concurrent Thread Stack Scanning),根据SPECjbb2015测试结果,实现并发线程栈扫描之后,ZGC的STW时间又能降低一个数量级,停顿时间将进入毫秒时代。

ZGC是一款优秀的垃圾收集器,它借鉴了Pauseless GC,也似乎在朝着C4 GC的方向发展——引入分代思想。

ZGC卓越的表现,让我们开发者看到了商用级别的GC“飞入寻常百姓家”的希望,随着JDK的发展,我相信在未来的某一天,JVM调优这种反人类的操作将不复存在,底层的GC会自适应各种情况自动优化。

总结

ZGC是Java的最前沿的技术的代表。

ZGC追求低停顿时间,并将此做到极致,虽然牺牲了一部分的性能,但完全可以接受。其中的染色指针技术和多重映射思想也值得我们学习。

ZGC多个视图之间的切换,某个瞬间,我看到了电影《信条》的影子。

在G1都没有普及的今天,谈论ZGC似乎为时过早。但不管怎么样,ZGC都是一款优秀的垃圾收集器,值得我们去学习。

[1] 参考文章

: https://mp.weixin.qq.com/s/ag5u2EPObx7bZr7hkcrOTg

[2] 参考文章

: https://wiki.openjdk.java.net/display/zgc/Main

[3] 参考文章

https://www.usenix.org/legacy/events/vee05/full_papers/p46-click.pdf

[4] 参考书籍:《新一代垃圾回收器ZGC设计与实现》

—————END—————推荐阅读:
  • 简单、易用的 MySQL 官方压测工具

  • “12306”的架构到底有多牛逼?

  • 新技能 MyBatis 千万数据表,快速分页!

  • 一文搞懂 Redis 缓存穿透、击穿、雪崩!

  • MyBatis-Plus常用 API 全套教程

  • 腾讯,干掉 Redis 项目,正式开源、太牛逼啦!

  • IntelliJ IDEA 更新后,电脑卡成球,该如何优化?

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。
明天见(。・ω・。)ノ♡

这篇关于Java最新前沿技术:ZGC垃圾收集器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/339779

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听