Oracle即将发布的全新Java垃圾收集器 ZGC

Java 11的特性集合已经确定,其中包含了一些非常棒的特性。新版本提供了一个全新的垃圾回收器ZGC,它由甲骨文开发,承诺在TB级别的堆上实现非常低的停顿时间。在本文中,我们将介绍甲骨文开发ZGC的动机、ZGC的技术概览以及ZGC带来的一些非常令人兴奋的可能性。

\\

那么为什么要开发ZGC?毕竟Java 10中已经带有4款久经考验的垃圾回收器。Hotspot最新的垃圾回收器G1是在2006年推出的。当时最大的AWS实例是m1.small,配备1个vCPU和1.7GB内存,而到了今天,AWS提供了x1e.32xlarge实例,配备了128个vCPU和令人难以置信的3,904GB内存。ZGC所针对的是这些在未来普遍存在的大容量内存:TB级别的堆容量,具有很低的停顿时间(小于10毫秒),对整体应用性能的影响也很小(对吞吐量的影响低于15%)。ZGC所采用的机制也可以在未来进行扩展,以支持一些令人兴奋的特性,如多层堆(用于热对象的DRAM和用于低频访问对象的NVMe闪存)或压缩堆。

\\

GC术语

\\

要了解ZGC在现有垃圾回收器中所处的位置,以及它是如何达到这个位置的,我们先需要先了解一些术语。最基本的GC包括识别出不再使用的内存,并将其变为可用的。现代垃圾回收器通常分几个阶段来完成回收过程,如下所示:

\\
  • 并行(Parallel)——运行中的JVM包含应用程序线程和GC线程。在并行阶段,会运行多个GC线程,也就是说任务被拆分给它们去完成。至于GC线程是否可以与正在运行的应用程序线程重叠,这个在规范中并没有特别说明。\\t
  • 串行(Serial)——串行阶段只有单个GC线程在运行。与上面的并行阶段一样,规范中也没有说明GC线程是否可以与当前运行的应用程序线程重叠。\\t
  • Stop The World(STW)——在这个阶段,应用程序线程被暂停,让GC线程执行它们的任务。当你遇到GC停顿时,说明虚拟机进入了STW阶段。\\t
  • 并发(Concurrent)——在并发阶段,GC线程可以在运行应用程序线程的同时执行自己的任务。并发阶段非常复杂,因为应用程序线程有可能在GC完成之前将其中断。\\t
  • 增量(Incremental)——在增量阶段,它可以运行一段时间,并基于某些条件提前终止,例如时间预算或执行更高优先级的GC阶段。\

权衡取舍

\\

需要指出的是,所有这些属性都存在权衡。例如,并行阶段将利用多个GC线程来执行任务,但这样做会导致协调线程的开销。同样,并发阶段不会暂停应用程序线程,但可能涉及更多的开销和复杂性。

\\

ZGC

\\

在了解了GC不同阶段的属性后,现在让我们来探讨ZGC的工作原理。ZGC使用了两项新技术:彩色指针和加载屏障。

\\

指针着色

\\

指针着色是将信息存储在指针(或引用)中的一种技术。这是有可能的,因为在64位平台上(ZGC仅支持64位),指针可以处理比系统实际拥有的内存更大的内存,因此可以使用多余的位来存储状态。ZGC将堆限制为4TB,需要42位,剩下的22位当中目前已经使用了4位:finalizable、remap、mark0和mark1。

\\

不过,指针着色也存在一个问题,当你想要取消引用指针时,需要做额外的工作,因为你需要屏蔽掉信息位。SPARC平台已经为指针屏蔽提供了内置硬件支持,所以这不是什么问题。但x86平台还没有提供类似的支持,所以ZGC团队针对x86平台使用了多次映射技术。

\\

多次映射

\\

要了解多映射的工作原理,我们需要先简要地解释一下虚拟内存和物理内存之间的区别。物理内存是系统可用的实际内存,也就是DRAM芯片的容量。虚拟内存是抽象的,对于应用程序来说,它们有自己的物理内存试图(通常是隔离的)。操作系统负责维护虚拟内存和物理内存之间的映射,通过使用页表和处理器的内存管理单元(MMU)以及转换后备缓冲区(TLB,用于转换应用程序的请求地址)来实现。

\\

多次映射技术将不同范围的虚拟内存映射到同一物理内存上。在remap、mark0和mark1当中,同一时间点只能有一个为1,因此可以使用三个映射。ZGC源代码中提供了一个很直观的图表(http://hg.openjdk.java.net/zgc/zgc/file/59c07aef65ac/src/hotspot/os_cpu/linux_x86/zGlobals_linux_x86.hpp#l39)。

\\

加载屏障

\\

加载屏障是一小段代码,当应用程序线程从堆加载引用时就会运行这段代码(即访问对象的非原始类型字段):

\\
\void printName( Person person ) {\    String name = person.name;  // 将会触发加载屏障,因为从堆中加载了一个引用\    System.out.println(name);   // 没有直接使用加载屏障\}
\\

第一行代码是给变量name赋值,这需要跟踪堆上的person引用,然后再加载name引用。这个时候会触发加载屏障。第二行代码在屏幕上打印name,不会直接触发加载屏障,因为不需要加载堆引用——name是局部变量,因此不需要从堆加载引用。不过,System和out,或者println内部可能会触发其他加载屏障。

\\

这与其他垃圾回收器(例如G1)使用的写入屏障形成对比。加载屏障的任务是检查引用的状态,并在将引用(或者不同的引用)返回给应用程序之前执行一些任务。在ZGC中,它会对加载的引用进行测试,查看是否设置了某些位,具体取决于当前处于哪个阶段。如果引用通过测试,就不执行任何其他操作,如果没有通过,就会在将引用返回给应用程序之前执行一些特定于当前阶段的操作。

\\

标记

\\

在了解了这两项新技术后,现在让我们来看看ZGC的GC周期。GC周期的第一部分是标记,就是以某种方式查找并标记应用程序可以访问到的所有堆对象,换句话说,就是查找非垃圾对象。

\\

ZGC的标记分为三个阶段。第一阶段是STW,在这一阶段,GC root被标记为存活。GC root类似于局部变量,应用程序使用它们来访问堆上的其他对象。从GC root开始遍历对象图,如果某些对象无法被访问到,那么应用程序也就无法访问到这些对象,它们就被认为是垃圾。可以从GC root访问到的对象集被称为存活集。GC root标记步骤所需要的时间非常短,因为GC root的总量通常相对较少。

\\

c47849eb54aaee8f3a45b4cf6c517950.png

\\

标记阶段完成后,应用程序恢复运行,而ZGC将开始下一阶段,发遍历对象图,并标记所有可访问的对象。在这一阶段,加载屏障会检查所有已加载的引用,看看它们的掩码是否已经针对这一阶段进行过标记,如果尚未标记,就将其添加到待标记队列。

\\

在完成这一步后,会出现一个短暂的STW阶段,它会处理一些边缘情况,然后整个标记过程就完成了。

\\

重定位

\\

GC周期的下一个主要部分是重定位。重定位就是要移动存活对象,以便释放部分堆空间。为什么要移动对象而不是填补空隙?有些GC确实是这样做的,但这样会造成不好的后果,即堆分配将变得非常昂贵,因为在分配堆空间时,分配器需要找到放置对象的空闲空间。相反,如果可以释放大块内存,堆空间分配就会变得很简单,只需要将指针按照对象所需的内存量进行递增就可以了。

\\

ZGC将堆分成页,在开始进行重定位时,它会选择一组需要重新定位的存活对象的页。在选择好重定位集后,会出现一次STW停顿,ZGC对重定位集中的对象进行重定位,并重新映射它们对新地址的引用。与之前的STW一样,停顿时间取决于root的数量以及重定位集与存活集的比率,这个比率通常都很小。它不会随着堆大小的变化而变化,这与其他大部分垃圾回收器一样。

\\

移动完root之后,下一阶段是进行并发重定位。在这个阶段,GC线程遍历重定位集,并重新定位页中的所有对象。如果应用程序线程尝试加载重定位集中的对象,但这些对象还未被重定位,那么应用程序线程也可以对它们进行重定位,这是通过加载屏障来实现的,如下面的流程图所示:

\\

4eb8e4d3e4c8187f19b052f1aba30582.png

\\

这样可以确保应用程序看到的所有引用都是最新的,并且应用程序不会对正在被重定位的对象做任何操作。

\\

GC线程最终会重定位重定位集中的所有对象,不过仍然可能存在一些指向这些对象旧地址的引用。GC会遍历对象图,并将所有这些引用重新映射到新的地址上,但这是一个非常昂贵的步骤。所以,这一步被并入到下一个标记阶段。在标记期间,如果发现未重新映射的引用,则将其重新映射,并标记为存活。

\\

回顾

\\

试图单独理解复杂的垃圾回收器(如ZGC)性能特征是很困难的,但有一点是很清楚的,我们在文中所提到的GC停顿都与GC root有关,而与存活对象集、堆大小或垃圾对象没有关系。标记阶段的最后一次停顿是一个例外,它是增量进行的,而且如果超过时间预算,GC将恢复到并发标记,直到下一次进行尝试。

\\

性能

\\

那么ZGC的性能如何?ZGC的SPECjbb 2015吞吐量数据与Parallel GC(为吞吐量进行过优化)大致相当,平均停顿时间为1毫秒,最长为4毫秒。这与平均停顿时间超过200毫秒的G1和Parallel形成鲜明的对比。

\\

未来的可能性

\\

彩色指针和加载屏障为我们带来了一些有趣的未来可能性。

\\

多层堆和压缩

\\

随着闪存和非易失性内存变得越来越普及,JVM的多层堆将成为可能,在多层堆中,很少被访问的存活对象将被保存在较慢的内存层中。

\\

我们可以对指针元数据进行扩展,加入一些计数器位,并使用这些位信息来决定是否需要移动对象。在需要使用对象的时候,可以通过加载屏障从相应的内存层获取对象。

\\

或者也可以不将对象重定位到较慢的内存层,而是将对象保存在主内存中,不过需要对其进行压缩。在请求对象时,通过加载屏障解对其进行解压并分配到堆中。

\\

ZGC的状态

\\

在撰写本文时,ZGC还处在实验阶段。读者可以通过Java 11 Early Access版本(http://jdk.java.net/11/)来体验ZGC,但需要指出的是,要解决一个新垃圾回收器存在的所有问题可能需要很长的一段时间。G1从发布到脱离实验阶段花了至少三年时间。

\\

总结

\\

服务器拥有数百GB甚至是数TB的内存变得越来越普及,Java有效使用内存堆的能力变得越来越重要。ZGC是一个令人兴奋的新型垃圾回收器,致力于大幅降低大堆垃圾回收的停顿时间。它通过使用彩色指针和加载屏障来实现这一点,它们都是Hotspot新引入的GC技术,并带来了一些有趣的未来可能性。ZGC将作为Java 11的实验性垃圾回收器,读者现在可以通过Java 11 Early Access体验ZGC。

\\

英文原文:https://www.opsian.com/blog/javas-new-zgc-is-very-exciting/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282162.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何获取 OSS AccessKeyId、AccessKeySecret

开通阿里云oss:https://www.aliyun.com/product/oss 1、点击概览 — AccessKey 注:官方链接 2、出现下图,选择“开始使用子用户Access Key” 3、填写用户名,并点击确定 4、这时会给你的手机发送验证码确定是本人操作,…

【网络爬虫入门02】HTTP客户端库Requests的基本原理与基础应用

【网络爬虫入门02】HTTP客户端库Requests的基本原理与基础应用 广东职业技术学院 欧浩源 2017-10-15 1、引言 实现网络爬虫的第一步就是要建立网络连接并向服务器或网页等网络资源发起请求。urllib是目前最常用的做法,然而Requests会比urlib更加方便,能…

《零信任的基石:使用 SPIFFE 为基础设施创建通用身份》翻译电子书分享

国庆假期除了去浙江和安徽玩了一圈欣赏江南山水和徽派建筑之外,还抽空翻译了一本电子书。本书译自 Solving the Bottom Turtle — a SPIFFE Way to Establish Trust in Your Infrastructure via Universal Identity。你可以选择在线阅读(推荐&#xff09…

《Outlook时间整理术》一创建和使用自己的文件夹结构

本节书摘来异步社区《Outlook时间整理术》一书中的第1章,作者: 【德】Lothar Seiwert , Holger Woeltje 译者: 欧阳宇,更多章节内容可以访问云栖社区“异步社区”公众号查看。 创建和使用自己的文件夹结构 花费约两小时为电子邮件…

《实用软件架构:从系统环境到软件部署 》——2.4 架构视图与架构视点

本节书摘来自华章出版社《实用软件架构:从系统环境到软件部署》一书中的第2章,第2.4节,作者:[印]蒂拉克米特拉(Tilak Mitra)著,爱飞翔 译,更多章节内容可以访问云栖社区“华章计算机…

TCP:当初取代NCP,如今害怕被取代

我叫TCP(Transmission Control Protocol)也叫传输控制协议。不觉回忆1983年,亲手将NCP协议淘汰,取而代之的是我,成了火遍大江南北的网络红人之一。现如今,我感受到前所未有的恐惧,因为我一生的敌…

批处理中的IF详解

在CMD使用IF /?打开IF的系统帮助会发现IF有3种基本的用法! 第一种用法:IF [NOT] ERRORLEVEL number command 这种用法现在很少用了,因为它需要使用到CHOICE命令,这个命令现在被set /p代替了,它是判断CHOICE命令选择的选项的&…

fullcalendar 显示的时间间隔只有四十五分钟_【体能新视点】——女子篮球运动员比赛期间的心率、血乳酸浓度和时间运动分析...

女子篮球运动员比赛期间的心率、血乳酸浓度和时间-运动分析—摘要—本研究的目的是检验女子篮球运动员在比赛规则改变后的生理需求和运动模式。在九场正式比赛中,对九名大学代表队队员进行了研究。每场比赛都被录像以确定主要动作的频率,连续记录心率&am…

《JavaScript机器人编程指南》——1.2 NodeBot是什么,基本词汇还有哪些

本节书摘来异步社区《JavaScript机器人编程指南》一书中的第1章,第1.2节,作者:【美】Kassandra Perch(珀芝),更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.2 NodeBot是什么,基本词…

解决在Python中使用Win32api报错的问题,No module named win32api

一、系统环境 操作系统: Win7 64位 Python:3.7.0 二、在使用import win32api时,报错:No module named win32api 网上查到有下面解决办法: 方法1:pip install pypiwin32或pip3 install pypiwin32 或 python -m pip install pypiwin…

《Python游戏编程入门》——1.2 初识Python

本节书摘来自异步社区《Python游戏编程入门》一书中的第1章,第1.2节,作者[美]Jonathan S. Harbour ,李强 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.2 初识Python Python既是一个软件工具包,也是一种…

大数据:Parquet文件存储格式

一、Parquet的组成 Parquet仅仅是一种存储格式,它是语言、平台无关的,并且不需要和任何一种数据处理框架绑定,目前能够和Parquet适配的组件包括下面这些,可以看出基本上通常使用的查询引擎和计算框架都已适配,并且可以…

“数据门”事件频发 如何避免人为因素导致数据泄露?

前段时间,某酒店集团数据泄露引起轩然大波,泄露的数据中包含了用户姓名、手机号、邮箱、号等多项信息。卖家对这个约5亿条数据打包出售价格为8比特币或520门罗币。而关于此次信息泄露事件的原因,目前尚未定论。据悉,由于集团某程序…

lamda获取参数集合去空_JAVA集合框架知识

1. Vector用法和ArrayList区别(1) Vector的特有方法有哪些?void addElement(E obj) 将指定的组件添加到此向量的末尾,将其大小增加1。(2) Vector与ArrayList的区别是什么?①Vector的add()方法是同步方法,ArrayList的add()方法是非…

Blazor学习之旅(2)第一个Blazor应用

【Blazor】| 总结/Edison Zhou大家好,我是Edison。最近在学习Blazor做全栈开发,因此根据老习惯,我会将我的学习过程记录下来,一来体系化整理,二来作为笔记供将来翻看。本篇我们来构建第一个Blazor Web应用,…

HTTP与服务器的四种交互方式

Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而H…

ubuntu 以太网已连接但是无法联网_工业以太网有多“牛X”?两个案例告诉你

现代生活中,工业以太网发挥的作用愈来愈重要。为增进大家对工业以太网的认识,本文将基于3方面介绍工业以太网:何为工业以太网物理层?工业以太网具有哪些优势?工业以太网两大方案介绍。如果你对工业以太网具有兴趣&…

http服务详解(1)——一次完整的http服务请求处理过程

前言:要熟练掌握一个服务,首先需要非常了解这个服务的工作过程,这篇就详细解释了http服务的请求处理过程。 一次完整的http请求处理过程 (1)流程图 (2)过程详解 0、DNS域名解析:递归…

线程管理(九)使用本地线程变量

声明:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernndez Gonzlez 译者:郑玉婷 校对:方腾飞 使用本地线程变量 并发应用的一个关键地方就是共享数据。这个对那些扩展Thread类或者实现Runnable接…

5训练需要更改参数吗_糖尿病病人需要多喝水吗?多喝水的5大好处,了解一下...

糖尿病是典型的慢性疾病,需要在生活中多加讲究,从各方面进行预防,其中喝水也是非常重要的一项调控方法。这就与糖尿病影响到患者的代谢功能有一定的关系,如果能够保持良好的饮水习惯,就可以有效解决糖尿病带来的影响。…