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的总量通常相对较少。

\\

\\

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

\\

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

\\

重定位

\\

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

\\

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

\\

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

\\

\\

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

\\

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/

Oracle即将发布的全新Java垃圾收集器 ZGC相关推荐

  1. java垃圾收集器zgc,ZGC简介:可扩展的低延时JVM垃圾收集器

    注:JAVA自动垃圾收集器是把双刃剑,既带来了无数开发便利,也引入了不少StopTheWorld这样的性能问题.如何解决这些问题,且看ZGC! 1.简介 今天,应用程序同时为数千甚至数百万用户提供服务 ...

  2. (七)Java垃圾收集器详解

    面试官问:Java垃圾收集器了解过多少,说一下 JVM 有哪些垃圾回收器?这些问题在你面试高级Java的时候经常会问到.本篇文章结合着[深入理解Java虚拟机]一书当中整理了本篇博客. 如果想要对收集 ...

  3. 【转】Java垃圾收集器

    原文链接 http://www.cnblogs.com/gw811/archive/2012/10/19/2730258.html#top Java垃圾收集器 概述 说起垃圾收集(Garbage Co ...

  4. hotspot 垃圾收集器_HotSpot增量Java垃圾收集器

    hotspot 垃圾收集器 在我最近的博客文章" 确定活动的HotSpot垃圾收集器"中 ,我描述了可用于确定HotSpot JVM (Java进程)正在使用的垃圾收集器(当从命令 ...

  5. HotSpot增量Java垃圾收集器

    在我最近的博客文章" 确定活动的HotSpot垃圾收集器"中 ,我描述了可用于确定HotSpot JVM (Java进程)正在使用的垃圾收集器(当从命令行参数(标志) 中看不出来) ...

  6. Java垃圾收集器:G1GC何时将CMS强制退出?

    在针对JDK 9(2017/4/4)提出的JEP中 , Mark Reinhold写道JEP 291 ("弃用并发标记扫描(CMS)垃圾收集器")是"已被放入" ...

  7. 再谈GC2:Java垃圾收集器与GC日志分析实践

    4. GC 算法(实现篇) - GC参考手册 2017年02月05日 23:58:36 阅读数:6862 您应该已经阅读了前面的章节: 垃圾收集简介 - GC参考手册 Java中的垃圾收集 - GC参 ...

  8. 深入理解JVM(5) : Java垃圾收集器

    如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现. Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商.不同版本的虚拟机所提供的垃圾收集器都可能会有很大差 ...

  9. 关于Java 垃圾收集器你应该知道这些

    如果Java虚拟机中标记清除算法.标记整理算法.复制算法.分代算法这些属于GC收集算法中的方法论,那么"GC收集器"则是这些方法论的具体实现. 概念准备 下面了解几个概念以帮助后面 ...

最新文章

  1. Linux下的hostname命令详解
  2. 不知道新三字经是啥?男默女泪的扫盲科普帖来了
  3. TensorFlow实战3——TensorFlow实现CNN
  4. 详解Java中的正则表达式
  5. 全网首次公开!iOS14 到来前,开发者必需的时间表及 To do list
  6. java拼写json
  7. 测试思路系列:《谷歌的软件测试之道》读书笔记
  8. 利用cad计算型材的弹性模量_型材截面特性的CAD计算方法
  9. Citrix XenApp/XenDesktop 7.15 LTSR发布
  10. STM32 标准外设库(STM32F107)
  11. Java小白之编写:计算个税的程序
  12. 西瓜书-机器学习复习<HENU>
  13. 自然语言处理是什么,我们为什么需要处理自然语言?
  14. hgame 2022 逆向 reverse 部分题目 Writeup
  15. 女孩都喜欢什么类型的男生?
  16. 甲乙分别自A,B两地同时相向步行,2小时后中途相遇,甲乙步行速度都提高了1千米/时,当甲到达B地后立刻按原路向A地返行,当乙到达A地后也立刻按原路向B地返行,甲乙两人在第一次相遇后3小时36分
  17. IO IR 个人作业汇总——康熙
  18. linux系统漏洞测试过程,Linux下bash破壳漏洞检测方法
  19. 【愚公系列】2023年04月 攻防世界-MOBILE(Android2.0)
  20. 结束php语句的正确方法是,结束 PHP 语句的正确方法是?

热门文章

  1. java dcl 失效解决_DCL失效原因和解决方案
  2. 架构师之路 扩充字段_扩大您作为设计师的业务影响力的四个基础
  3. 提升效率的Vue组件开发和实战技巧
  4. linux PROC文件系统详解
  5. python爬虫笔记(七):实战(三)股票数据定向爬虫
  6. Ionic Mac 环境配置
  7. Linux下c/c++项目代码覆盖率的产生方法
  8. RedHat el5.0 搭建 Postfix 邮件服务器系统一
  9. java contains 通配符_java删除文件支持通配符
  10. 2018.12.08 codeforces 946D. Timetable(背包)