深刻了解 JVM – Shenadoah
前言
zgc 和 shenadoah 的收集器是面向未来的收集器,目前还处于不断完善的阶段,尽管咱们平时可能不太用的上,然而理解和根本把握它是必须的,对于这一块网上的内容的确比拟少,所以集体还是应用了书本外面的内容进行总结。
另外这两个垃圾收集器是齐全舍弃分代这个概念的,留神是齐全舍弃,并不是相似 G1 收集器尽管应用了分区然而实质上还是分代收集的收集器。
因为这两个收集器的内容较多,这里离开进行解说,本篇解说 Shenadoah 收集器。
思维导图:
不想看文字的,能够查看思维导图:https://www.mubucm.com/doc/7L…
概述
低提早垃圾收集器
在正式介绍之前,有必要阐明一下整个背景,古代的垃圾收集器思考的点次要为上面这三个条件:内存占用,吞吐量,提早,通过之前的收集器介绍,咱们晓得了尽管支流的 g1 收集器在标记阶段实现了并发,然而在初始标记和筛选回收阶段还是须要进行阶段性的 stop world 的,这个垃圾收集器并没有做到真正意义上的并发,并且因为分区 +region 分代的设计限度,必然会产生垃圾收集的进展。所以将来的垃圾收集器次要指标将会是面向极低提早进军,也就是努力实现用户线程和垃圾收集器线程的齐全并发运行。
值得一提的是尽管新生的低提早垃圾收集器摈弃了分代的概念,然而 G1 的 Region 分块以及垃圾进展模型保留了下来,咱们也能够看到简直所有的垃圾收集器都是基于前人的致力成绩进行改良,所以不须要非常恐怖内容很难或者是齐全颠覆想法。
g1 和 cms 实现并发的细节
之前的文章提到了 增量更新 和原始快照,cms 应用的是增量更新,g1 应用的是原始快照,另外 cms 应用标记 - 革除的算法,免不了内存碎片,而 g1 尽管应用标记 - 整顿,然而究竟还是须要进行暂停的,所以这是一个十分辣手的问题。
Shenadoah
简介
这款收集器是首款非 jdk 官网开发的垃圾收集器,由 redhat 公司开发,后续被捐献给 eclipse 基金会,目前由 eclipse 基金会进行保护和治理。尽管 Shenadoah 从设计的细节来看有很多须要欠缺的中央,然而的确曾经具备了独立作为垃圾收集器应用的条件。
比拟惋惜的是 oracle 因为商业竞争的问题会把 shenandoah 通过条件编译的伎俩进行排除应用比拟麻烦,所以 shenadoah 只能存在于 openJDK 无奈在 OracleJdk 上进行部署,然而这款垃圾收集器仍然值得咱们学习。
特点
上面来说一下 shenadoah 的特点:
Region
和 G1 收集器的设计原理一样应用的是 region 进行分块,同样有着大对象的概念,默认的策略也是依据算法回收最有价值的 region。
没有分代和连贯矩阵
留神是没有分代的概念,默认不应用分代收集,换言之就是没有新生代和老年代的说法。那要怎么设计?Shenandoah 的解决方案是应用独立构建的“连贯矩阵”全局数据结构来保护 region 的援用关系,也不要被连贯矩阵这种名词给吓到了,其实实质上是一个 二维数组 构造,比方咱们在 Region N 援用了Region M,那么就会在对应的 N 行 M 列上打上一个标记,也就是说全局的对象援用都会通过这个表来保护,这也意味着连贯矩阵会随着对象的增长一直收缩。
G1 收集器是放弃固定分代而是应用分区的设计,然而分区实质上还是分代的,只不过能够自在决定属于哪一个分代。
上面间接从书外面拷了一张图来显示连贯矩阵的设计:
不得不说的是这个连贯矩阵在设计上是仁者见仁智者见智了,保护一个矩阵尽管很不便然而随着对象的增多会呈现出指数性的表收缩,这样来看还是一个值得商讨的设计,这一点在后续的垃圾收集器 zgc 介绍中会提到,zgc 发现了连贯矩阵的问题,采纳了一些改良伎俩来解决表收缩的问题。
反对并发收集和整顿
反对并发收集和整顿,能够实现标记和整顿阶段齐全和用户线程并发执行。
算法细节
那么这个收集器是如何做到这些事件的呢,在介绍工作流程之前,咱们来聊一下算法的实现细节。
brooks pointer
历史起因不过多介绍,这里阐明一下这个值的含意:转发指针。转发指针是什么呢?它是用来解决对象挪动和用户程序并发的一种解决方案。
Brooks pointer 的工作原理:就是在对象的构造布局上减少一个新的援用字段,这个援用 通常状况下指向本人 ,当对象产生转移的时候,brooks pointer 会指向 新援用的地址,这样指向旧援用的对象就能够修复援用指向新对象,这种构造在模式上和 JVM 的句柄定位相似,都是应用一种间接的拜访模式,差异是转发指针会扩散存在对象头外部。(之前咱们探讨过对象头是动静扩大的格局)
这种设计模式也有点相似于链表的设计模式。
补充:之前如何解决对象援用问题?
应用的是一种在原有的对象内存之上 设置爱护陷阱 + 异样解决 的形式,一旦呈现拜访旧对象的行为,就会进入到爱护陷阱当中,并且进入异样处理器进行代码逻辑和援用的修复。这种形式看起来非常的无效,然而如果没有操作系统的反对,就须要通过一直的 用户态到内核态的切换,须要消耗更多的上下文切换资源,也是一种十分消耗性能的斗争方法。
毛病:
尽管转发指针被优化到只有一行汇编指令的水平,然而仍然要耗费对象拜访的效率,当然这个计划毫无疑问是比内存陷阱要好,
并发问题:
转发指针的设计意味着他必然有并发的问题,如果产生并发操作,就须要保障写操作必须是在新复制的对象上面,无妨思考上面的问题:
- 收集器线程复制了新的对象正本
- 用户线程更新对象的某个字段
- 收集器线程更新转发指针的援用值为新正本地址。
如果不防备这三个问题,就会导致用户线程的对象变更都是操作旧对象,所以必须针对指针的拜访操作采取同步的措施。解决办法和对象的援用调配形式也是相似的也是应用CAS+ 更新失败重试的操作机制。
最初,还须要留神的是 Shenadoah 必须应用读写屏障去保护 brooks pointer(并发问题决定了要时刻放弃同步),这个代价是十分大的。上面咱们接着来讲讲读写屏障的问题。
读写屏障
shenandoah 不仅应用了写屏障还应用了读屏障,读屏障也是相似对象援用操作的一个 AOP 的切面,咱们都晓得对象的读操作必定是要多于写操作的,所以应用读屏障的代价要大很多。
写屏障的概念能够看专栏之前的文章:深刻了解 JVM – Hotspot 算法细节 #写屏障
当然 Shenandoah 开发者也意识到这个问题,在 JDK13 的 版本中,改用了基于“援用拜访屏障”的形式解决读屏障的问题,“援用拜访屏障”指的是只拦挡对象相似是援用类型的数据进行拜访屏障的拦挡,这样就能够省去一些原生类型并发批改拜访的操作,缩小宏大的读屏障保护开销。
从这里也能够看进去 Redhat 的开发团队在设计 jvm 垃圾收集器上的教训不足,然而能够及时调整解决问题。
工作过程:
shenandoah 的工作步骤能够划分为 9 个步骤,最新版本的 shenandoah 还在初始标记的步骤后面减少了三个步骤,简略了解为分代收集当中的 Minor GC 操作即可。
接下来说一下具体的步骤:
- 初始标记:和 G1 一样,首先标记出所有的 GC ROOT 关联的对象,留神这个阶段是须要进展的。
- 并发标记 :和 G1 一样,依据 GC ROOT 遍历对象图,标记出所有的可达对象,这个阶段 和用户线程一起并发。
- 最终标记:还是和 G1 一样,解决剩下的对象扫描操作,同时计算出回收价值最高的 Region,最终标记阶段有 一小段的暂停。
- 并发清理:这个阶段用于清理整个区域一个存活对象都没有的 Region,这个阶段是 并发执行 的。
- 并发回收 : 外围差别点,在这个阶段,会把回收集外面存活对象先复制一份到到其余未应用的 Region,然而要留神这个操作并不是同步的而是和用户线程并发的,再次强调是并发的,不是和 G1 的交替的暂停和运行的工作形式,留神这里的实现原理就是之前说的“Brooks Pointer”,同时应用 转发指针的操作 +cas 锁 将旧对象的援用修复为新对象的形式。这个阶段也是和 G1 最大的区别,实现了垃圾回收和用户线程的并发操作。
- 初始援用更新:并发回收阶段复制对象完结之后,还须要把堆中的所有指向旧对象的更新到复制之后的新地址,这个操作也叫做 援用更新 。同样会产生一个 十分短暂的进展。
- 并发援用更新:真正开始援用更新的操作,工夫长短取决于援用的多少。毫无疑问也是并发执行的
- 最终援用更新:解决堆中援用更新之后,修改 GC ROOT 援用,这个阶段是最初一次进展,进展工夫和 GC Roots 的数量无关。
- 并发清理:最初回收没有任何对象的空 Region
并发标记、并发回收、并发援用更新这三个阶段是最重要的,重点记忆即可。
上面的图是从官网的 wiki 扒过去的:
Init Mark:初始标记
Final Mark:最终标记
Init-UR:初始援用更新
Final-UR:最终援用更新
后果比照
官网有一张比照图来显示 Shenandoah 的垃圾收集耗时比照,从图中能够看到做到了简直无提早的垃圾收集:
总结
Shenadoah 收集器是收款非 JDK 官网开发的收集器,然而很遗憾的是,因为商业竞争关系,他只存在于 OpenJDK,没有被商用,并且后续因为更加 ZGC 的开发,Shenadoah 的作用也在逐步缩小,然而不得不抵赖的作为没有 JVM 垃圾收集器开发教训的开发者们开发的收集器,这款收集器满足了要求并且非常值得借鉴和学习。
另外能够看到即便是简化工作原理,古代的垃圾收集器也曾经十分复杂了,因为目前大部分开发者还是应用 JDK8 和 G1 等垃圾收集器,所以这些垃圾收集器在目前看来还是属于面向未来的收集器,然而毫无疑问咱们须要一直的学习。
其余材料:
Shenadoah 垃圾收集器官网介绍
Shenandoah 收集器的 JVM 参数案例:
java -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=passive -Xlog:gc
写在最初
这本书讲述这款垃圾收集器的内容算是比拟浅显,然而对于咱们理解这款收集器来说算是足够了。想要理解更多内容,集体倡议间接找下面提供的官网 WIKI 动手,毕竟开发进去的人对这个货色才是最理解的。