简介

  • Shenandoah GC 与 ZGC 同为新一代的低延迟收集器, 分别由RedHat和Oracle开发, 目前还在实验阶段, 尚未使用于生产环境。

  • GC的三项指标: Footprint(内存占用), Throughput(吞吐量) 与 Latency(延迟), 有点像CAP理论, 三者只能取其二。

  • 目前主流GC是G1, 而此两者的延时比G1低很多。

Shenandoah GC

  • 操作系统支持:

    • Linux, Windows, macOS, Solaris
  • 硬件支持:

    • x86_64

    • x86_32

    • AArch64

  • JDK支持(注意是OpenJDK的非Oracle Build, 因为Oracle不想为RedHat写的代码负责任)

    • JDK8, JDK11, JDK13
  • Shenandoah 与 G1

    • Shenandoah 可以说是G1的下一代继承者

      • 它们拥有相似的堆内存布局
      • 在初始标记, 并发标记等多阶段的处理思路上都高度一致, 甚至共享了一部分实现代码
    • Shenandoah 相比 G1的改进:

      • 堆内存管理方面:

        • 支持并发的整理算法, 能与用户线程并发

        • 默认不使用分代收集

        • 摒弃了在G1中耗费大量内存和计算资源去维护的记忆集(Memory Set), 改用名为连接矩阵(Connection Matrix) 的全局数据结构来记录跨Region的引用关系。从而降低了处理跨带指针时的记忆集维护消耗以及伪共享问题的发生概率。

          • 连接矩阵示意图(其实这张图好像错了, 第一行的X应该在[3, 1]的位置)

            • 说白了这就是个临接矩阵, 横坐标代表当前区域, 纵坐标代表当前区域引用的区域
            • 那么就是区域5引用了区域3, 而区域3又引用了区域1
  • Shenandoah GC的工作过程大致可划分为9个阶段

    • Initial Marking(初始标记)

      • 与G1一致, 首先标记与GC Roots直接关联的对象, 此阶段仍会"Stop The World", 但停顿时间与堆大小无关, 只与GC Roots的数量相关。
    • Concurrent Marking(并发标记)

      • 与G1一致, 遍历对象图, 标记出全部可达的对象, 此阶段是与用户线程一起并发的, 时间长短取决于堆中存活对象的数量以及图的结构复杂程度。
    • Final Marking(最终标记)

      • 与G1一致, 处理剩余的SATB扫描, 并在此阶段统计出回收价值最高的Region, 将这些Region构成一组回收集(Collection Set)。

      • 最终标记阶段也会有一小段短暂的停顿。

    • Concurrent Cleanup(并发清理)

      • 此阶段用于清理那些整个区域内连一个存活对象都没有找到的Region(Immediate Garbage Region)
    • Concurrent Evacuation(并发回收)

      • 在此阶段, Shenandoah要把回收集里面的存活对象先复制一份到其他未被使用的Region中。

      • 重点是复制对象过程不冻结用户线程而是与用户线程并行, 这一实现有很大的技术屏障, Shenandoah 通过读屏障和 Brooks Pointers(转发指针) 解决了此困难。

      • 并发回收阶段运行时间长短取决于回收集的大小。

    • Initial Update Reference(初始引用更新)

      • 并发回首阶段复制对象结束后, 还需要把堆中所有指向旧对象的引用修正到复制后的新地址, 此操作称为引用更新。

      • 实际上此阶段并没有做什么实际的处理, 只是为了建立一个线程集合点, 确保所有并发回收阶段中进行的收集器线程都已完成分配给它们的对象移动任务而已。

      • 初始引用更新时间很短, 会产生一个非常短暂的停顿。

    • Concurrent Update Reference(并发引用更新)

      • 真正开始引用更新操作, 此阶段是与用户线程并发的, 时间长短取决于内存中涉及的引用数量的多少。

      • 与并发标记不同, 不需要再沿着对象图来搜索, 只需要按照内存物理地址的顺序, 线性地搜索出引用类型, 把旧值改为新值即可。

    • Final Update Reference(最终引用更新)

      • 处理了堆中的引用更新后, 还需要修正存于GC Roots中的引用。

      • 此阶段是Shenandoah的最后一次停顿, 停顿事件只与GC Roots数量相关。

    • Concurrent Cleanup(并发清理)

      • 经过并发回收和引用更新之后, 整个回收集中所有的Region已再无存活对象, 这些Region都变成了Immediate Garbage Regions了。

      • 最后再调用一次并发清理过程来回收这些Region的内存空间, 供以后新对象分配使用。

  • 工作过程示意图

    • 白色: 自由空间

    • 蓝色: 新分配的空间

    • 绿色: 存活的对象

    • 黄绿白: 被选入回收集的存活对象

    • 黄黄白: 需要更新引用的被选入回收集的存活对象

ZGC

  • 可以说是Oracle抄了 Azul System的作业, ZGC 与 Azul System公司的PGC(Pauseless GC) 和 C4(Concurrent Continuously Compacting Collector) 在算法和实现原理上是高度相似的, 只存在术语称谓的区别。

  • 主要特征:

    • 基于Region内存布局
    • 不设分代
    • 使用了读屏障, 染色指针和内存多重映射等技术来实现可并发的标记-整理算法
    • 以低延迟为首要目标
  • ZGC也采用基于Region的堆内存布局, 但与它们不同的是, ZGC的Region具有动态性: 动态创建和销毁, 以及动态的区域容量大小。在x64硬件平台下, ZGC的Region可以有以下三类容量:

    • Small Region

      • 容量固定为2MB, 用于放置小于256KB的小对象。
    • Medium Region

      • 容量固定为32MB, 用于放置大于等于256KB但小于4MB的对象。
    • Large Region

      • 容量不固定, 可以动态变化, 但必须为2MB的整数倍, 用于放置4MB或以上的大对象。
      • 每个大型Region中只会存放一个大对象, 它虽名为Large Region, 但它的实际容量完全有可能小于中型Region。
      • 大型Region在ZGC的实现中是不会被重分配的, 因为复制一个大对象的代价非常高昂。
  • 染色指针技术

    • 在64位系统中, 理论可以访问的内存高达16EB。实际上基于需求, 性能, 和成本考虑, 在AMD64架构中只支持到52位(4PB)的地址总线和48位(256TB)的虚拟地址空间, 目前64位的硬件实际能够支持的最大内存只有256TB。此外操作系统还有自己的约束, 64Linux系统分别支持47位(128TB)的进程虚拟地址和46位(64TB)的物理地址空间, 64位的Windows系统只支持44位(16TB)的物理地址空间。

    • 虽然Linux下64位指针的高18位不能用来寻址, 剩余的46位指针所能支持的64TB内存在今天仍能够充分满足大型服务器需要。而ZGC则利用了剩下的46位指针的高4位提取出来用于存储四个标志信息。

      • 通过这些标志位, 虚拟机可以直接从指针中看到其引用对象的状态, 是否进入重分配集, 是否通过finalize()方法才能被访问到。
      • 由于进一步压缩了原本只有46位的地址空间, ZGC能够管理的内存不可以超过4TB。
    • 三大优势

      • 染色指针可以使得一旦某个Region的存活对象被移走之后, 此Region立即就能够被释放和重用掉, 而不必等待整个堆中所有指向该Region的引用都被修正后才能清理。
      • 染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量。
      • 染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据, 以便日后进一步提高性能。
    • 在Linux/X86-64平台上的ZGC使用了多重映射(Multi-Mapping) 将多个不同的虚拟内存映射到同一个物理内存地址上。

      • 任何的进程在进程自己看来自己的内存空间都是连续的, 但是计算机实际的物理内存并不是与该进程的内存是一一对应的。碎片化的物理内存可以映射成一个完整的虚拟内存, 同时应用可以申请比物理内存大的内存, 使得多个内存互不干扰, 使编译好的二进制文件的地址统一化......
    • ZGC运作过程

      • Concurrent Mark(并发标记)

        • 遍历对象图做可达性分析的阶段, 前后也要经过类似于G1, Shenandoah 的初始标记, 最终标记的短暂停顿。

        • 与G1, Shenandoah不同的是, ZGC的标记是在指针上而不是在对象上进行的, 标记阶段会更新染色指针中的Marked0、Marked1标志位。

      • Concurrent Prepare for Relocate(并发预备重分配)

        • 此阶段需要根据特定的查询条件统计出本次收集过程要清理哪些Region, 将这些Region组成重分配集(Relocation Set)。
      • Concurrent Relocate(并发重分配)

        • 是ZGC执行过程中的核心阶段, 此过程要把重分配集中的存活对象复制到新的Region上, 并为重分配集中的每个Region维护一个转发表(Forward Table), 记录从旧对象到新对象的转向关系。

        • 由于染色指针的存在, ZGC能仅从引用上就明确得知一个对象是否处于重分配集之中。如果用户线程此时并发访问了位于重分配集中的对象, 这次访问将会被预置的内存屏障截获, 然后立即根据Region上的转发表记录将访问转发到新复制的对象上, 并同时修正该引用的值, 使其直接指向新对象, 此即为Self-Healing(自愈)[只有第一次访问旧对象会陷入转发]。

      • Concurrent Remap(并发重映射)

        • 修正整个堆中指向重分配集中旧对象的所有引用。
        • 重映射清理这些旧引用的主要目的是为了不变慢, 并不是很迫切。
        • ZGC将并发重映射阶段要做的工作, 合并到了下一次垃圾收集循环中的并发标记阶段里去完成, 从而节省了一次遍历对象图的开销。

Shenandoah 与 ZGC的更多相关文章

  1. JVM 低延迟垃圾收集器 Shenandoah 和 ZGC

    本文部分摘自<深入理解 Java 虚拟机第三版> 概述 衡量垃圾收集器的三项指标分别是:内存占用.吞吐量和延迟.这三者共同构成一个"不可能三角",即一款优秀的收集器最多 ...

  2. JDK15正式发布,划时代的ZGC同时宣布转正

    你发任你发,我用Java8.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BA ...

  3. Java 9和Java 10的新特性

    http://www.infoq.com/cn/news/2014/09/java9 Java 9新特性汇总 继2014年3月份Java 8发布之后,Open JDK加快了开发速度, Java 9的发 ...

  4. JDK 15 JAVA 15的新特性展望

    目录 JEP 371: Hidden Classes JEP 372: 删除 Nashorn JavaScript Engine JEP 377: 新的垃圾回收器ZGC正式上线了 JEP 378: T ...

  5. JVM垃圾回收(五)

    低延迟垃圾收集器 衡量垃圾收集器的三项最重要的指标是: 内存占用(Footprint).吞吐量(Throughput)和延迟(Latency).三者总体的表现会随技术进步而越来越好,但是要在这三个方面 ...

  6. 深入探究JVM之垃圾回收算法实现细节

    @ 目录 前言 垃圾回收算法实现细节 根节点枚举 安全点 安全区域 记忆集和卡表 写屏障 并发的可达性分析 低延迟GC Shenandoah ZGC 总结 前言 本篇紧接上文,主要讲解垃圾回收算法的实 ...

  7. 技术基础 | Apache Cassandra 4.0基准测试

    Apache Cassandra 4.0已经发布了Beta版,这是第一个支持JDK 11及更高JDK版本的Cassandra版本.   时延对于Apache Cassandra用户来说是个显而易见的关 ...

  8. JVM经典垃圾收集器

      这个关系不是一成不变的,由于维护和兼容性测试的成本,在JDK 8时将Serial+CMS. ParNew+Serial Old这两个组合声明为废弃(JEP 173),并在JDK 9中完全取消了这些 ...

  9. 《深入理解java虚拟机》第3版笔记3

    第3章 垃圾收集器与内存分配策略 可达性分析算法 在Java技术体系里面,固定可作为GC Roots的对象包括以下几种: 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使 ...

随机推荐

  1. maven搭建父子项目

    父工程:父工程又称为父控制器,只是一个简单的工程,不能单独运行.作用是将子模块跟子工程聚合在一起.父控制器中的pom.xml配置,在子模块跟子工程中都可以被继承. 子工程:项目中创建的具有业务逻辑并且 ...

  2. 16,div+css的布局较table布局有什么优点?

    改版的时候更加方便,只要改css文件 页面加载速度更快,结构化清晰,页面显示简洁 表现与结构相分离 易于搜索引擎优化,排名更靠前

  3. php集成环境、基础标记符

    集成环境:wamp windows apache mysql php lamp linux apache mysql php 标记符: 1.<?php ...... ?> 2.<?p ...

  4. 公用技术——面向对象领域——UML图——《The Unified Modeling Language User Guide》V2读书笔记——第一章节(建模的意义)

    第一章节到第三章节介绍UML的基本概念.第一章节主要介绍了UML语言的历史,介绍了建模的重要性(狗窝,房子,大厦),介绍了UML要实现哪些目标,在最后介绍了在使用UML语言时应该遵循的一些原则或者是规 ...

  5. centos610无桌面安装JDK

     Centos610系列配置 1.使用yum查找jdk: yum search java|grep jdk    2.选择安装截图中选中的版本 yum install java-1.8.0-openj ...

  6. HDU1285-确定比赛名次(拓扑+优先队列)

    对于拓扑排序,每次能入队的只有入度为0的点,所以用优先队列即可. 以及,第一组数据日常卡OJ,这组数据跳了一个点,我的程序这个版本也过不了(其实写了另一个版的),稍微改改更正确. #include & ...

  7. 【代码学习】PYTHON中的静态方法和类方法

    一.类方法 是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大 ...

  8. idea开发web项目${pageContext.request.contextPath}无法转义

    web-app版本问题,我的web.xml中头文件的配置是: <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web ...

  9. Nginx平滑升级版本!(重点)

    一.解释nginx的平滑升级 随着nginx越来越流行使用,并且nginx的优势也越来越明显,nginx的版本迭代也开始了加速模式,1.9.0版本的nginx更新了许多新功能,例如stream四层代理 ...

  10. 获取Linux系统运行情况信息

    代码: #include <stdio.h> #include <unistd.h> /* usleep() */ #include <stdlib.h> #inc ...