前言 |
第一篇文章说了Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS(传送门)这六种收集器,第二篇文章说了经典的G1收集器(传送门),今天我们还要单独的说一下低延迟收集器Shenandoah,Shenandoah收集器是一款只有OpenJDk才会包含的,这时候问题来了 什么是OpenJDK和OracleJDk呢?
OpenJDK是JDK的开放源码版本,在JDK11之后两者几乎一致,这篇文章写得比较清楚OpenJDK和OracleJDK的区别
Shenandoah与G1收集器比较 |
- Shenandoah和G1都采用相同的内存布局
- 默认的回收策略也一样,都是优先回收价值最大的Region
- Shenandoah摒弃了G1中耗费大量内存和计算资源去维护的记忆集
Shenandoah采用的是“连接矩阵”,连接矩阵是一个来记录跨Region的引用关系。可以理解为一张二维表 - 连接矩阵降低了处理跨代指针时的记忆集维护消耗
- 在回收阶段G1收集器不能与用户线程并发,而Shenandoah则可以和用户线程并发
Shenandoah收集器的工作过程 |
Shenandoah的工作过程可分为9步,但是这9步有和G1相似的地方我就不多阐述了,可以看我上一篇博客详细说了G1收集器
- 初始标记
和G1一样,标记GC Roots ,同样也是 Stop The World - 并发标记
和G1一样,标记处所有可达对象,这个阶段可与用户线程并发执行 - 最终标记
和G1一样,处理剩余的SATB(原始快照) - 并发清理
这个阶段是处理那些整个区域没有一个存活对象的Region - 并发回收
先把存活的对象移动到另一个Region区域中,但是这个过程是和用户线程并发的,就是出现用户线程访问旧对象地址的情况,Shenandoah用读屏障和“Brooks Pointers”的转发指针来解决,
什么是读屏障:写屏障是对象的引用发生变化是会记录下来,读屏障就是读出这个地址
什么是转发指针(Brooks是一个人名):当用户程序访问到旧对象的内存空间就会产生自陷中断,进入事先设定好的异常处理中,然后在转发的新的地址 - 初始引用更新
这个阶段没有把所有指向旧对象的引用修正为对象的新地址,而是仅仅确保并发回收阶段的收集线程都已经完成了任务,这个阶段需要非常短的停顿时间 - 并发引用更新
真正引用更新是这这个阶段,这个阶段是和用户线程并发执行的。 - 最终引用更新
这个阶段修正存在与GC Roots中的引用,也需要短暂的停顿时间 - 并发清理
回收这些标记的Region空间,供以后新对象分配使用
其实上面9个步骤中,最重要的就是并发标记、并发回收、并发引用更新 这三个阶段
总结 |
Shenandoah收集器作为第一款由非Oracle开发的垃圾收集器,表现出如此好的性能,基本上全部的工作过程都是并发的,初始标记和最终标记的停顿时间基本上是固定的,与堆的容量和堆中对象的数量没有正比例的关系!
看到这里, 你学废了吗?