深刻了解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的句柄定位相似,都是应用一种间接的拜访模式,差异是转发指针会扩散存在对象头外部。(之前咱们探讨过对象头是动静扩大的格局)

这种设计模式也有点相似于链表的设计模式。

补充:之前如何解决对象援用问题?

应用的是一种在原有的对象内存之上设置爱护陷阱+异样解决的形式,一旦呈现拜访旧对象的行为,就会进入到爱护陷阱当中,并且进入异样处理器进行代码逻辑和援用的修复。这种形式看起来非常的无效,然而如果没有操作系统的反对,就须要通过一直的用户态到内核态的切换,须要消耗更多的上下文切换资源,也是一种十分消耗性能的斗争方法。

毛病

尽管转发指针被优化到只有一行汇编指令的水平,然而仍然要耗费对象拜访的效率,当然这个计划毫无疑问是比内存陷阱要好,

并发问题

转发指针的设计意味着他必然有并发的问题,如果产生并发操作,就须要保障写操作必须是在新复制的对象上面,无妨思考上面的问题:

  1. 收集器线程复制了新的对象正本
  2. 用户线程更新对象的某个字段
  3. 收集器线程更新转发指针的援用值为新正本地址。

如果不防备这三个问题,就会导致用户线程的对象变更都是操作旧对象,所以必须针对指针的拜访操作采取同步的措施。解决办法和对象的援用调配形式也是相似的也是应用CAS+更新失败重试的操作机制

最初,还须要留神的是Shenadoah必须应用读写屏障去保护brooks pointer(并发问题决定了要时刻放弃同步),这个代价是十分大的。上面咱们接着来讲讲读写屏障的问题。

读写屏障

shenandoah不仅应用了写屏障还应用了读屏障,读屏障也是相似对象援用操作的一个AOP的切面,咱们都晓得对象的读操作必定是要多于写操作的,所以应用读屏障的代价要大很多。

写屏障的概念能够看专栏之前的文章:深刻了解JVM - Hotspot算法细节#写屏障

当然Shenandoah开发者也意识到这个问题,在JDK13的版本中,改用了基于“援用拜访屏障”的形式解决读屏障的问题,“援用拜访屏障”指的是只拦挡对象相似是援用类型的数据进行拜访屏障的拦挡,这样就能够省去一些原生类型并发批改拜访的操作,缩小宏大的读屏障保护开销。

从这里也能够看进去Redhat的开发团队在设计jvm垃圾收集器上的教训不足,然而能够及时调整解决问题。

工作过程:

shenandoah的工作步骤能够划分为9个步骤,最新版本的shenandoah还在初始标记的步骤后面减少了三个步骤,简略了解为分代收集当中的Minor GC操作即可。

接下来说一下具体的步骤:

  1. 初始标记:和G1一样,首先标记出所有的GC ROOT关联的对象,留神这个阶段是须要进展的
  2. 并发标记:和G1一样,依据GC ROOT遍历对象图,标记出所有的可达对象,这个阶段和用户线程一起并发
  3. 最终标记:还是和G1一样,解决剩下的对象扫描操作,同时计算出回收价值最高的Region,最终标记阶段有一小段的暂停
  4. 并发清理:这个阶段用于清理整个区域一个存活对象都没有的Region,这个阶段是并发执行的。
  5. 并发回收外围差别点,在这个阶段,会把回收集外面存活对象先复制一份到到其余未应用的Region,然而要留神这个操作并不是同步的而是和用户线程并发的,再次强调是并发的,不是和G1的交替的暂停和运行的工作形式,留神这里的实现原理就是之前说的“Brooks Pointer”,同时应用转发指针的操作+cas锁将旧对象的援用修复为新对象的形式。这个阶段也是和G1最大的区别,实现了垃圾回收和用户线程的并发操作。
  6. 初始援用更新:并发回收阶段复制对象完结之后,还须要把堆中的所有指向旧对象的更新到复制之后的新地址,这个操作也叫做援用更新。同样会产生一个十分短暂的进展
  7. 并发援用更新:真正开始援用更新的操作,工夫长短取决于援用的多少。毫无疑问也是并发执行的
  8. 最终援用更新:解决堆中援用更新之后,修改GC ROOT援用,这个阶段是最初一次进展,进展工夫和GC Roots的数量无关。
  9. 并发清理:最初回收没有任何对象的空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动手,毕竟开发进去的人对这个货色才是最理解的。