深入理解java虚拟机系列第三版读后小记:(六)低延迟垃圾收集器之Shenandoah收集器

深入理解java虚拟机系列第三版读后小记:六 低延迟垃圾收集器之Shenandoah收集器

前言

上文笔者介绍了jvm上经典的垃圾收集,本文将继续介绍低延迟的Shenandoah收集器。介绍Shenandoah收集器之前,首先要明白衡量jvm性能的三大尺标:吞吐量,延迟性,内存占用。当然,任何一款垃圾收集器都无法同时满足三个尺标,同时最高只能满足两个。而如今互联网的爆炸发展,对于延迟性的要求越来越高,人们更关注于低延迟的重要性,所以传统的垃圾回收器已无法满足互联网的要求,所以低延迟的回收器也愈发受到期待。

在这里插入图片描述
从图来看,深色段表示线程并发执行,不需要停止用户线程,所以显而易见的得只ZGC以及Shenandoah两款回收器stw的时间很短,几乎全程不需要停止用户线程,关于ZGC这款回收器放到下篇文章介绍。

Shenandoah简介

首先Shenandoah收集器目前只能运行在openJDK12以上,没错,官方商业版本的oracleJDK是不支持Shenandoah收集器的,究其原因网上流传的猜测排它,因为Shenandoah收集器并不是由oracle或者前sun公司的虚拟机团队开发出来的,而是由RedHat公司团队开发出来的,于2014年贡献到openJDK。具有官方正统的zgc收集器的功能还没有开源免费的Shenandoah功能多,也算是罕见的场景。
言归正传,Shenandoah收集器的布局和G1差不多,也是region堆,垃圾回收方式也差不多,根据回收价值和成本进行排序。当然读者可以认为Shenandoah就是一款增强版的G1,但其实还是和G1有区别的。
主要有三个区别:

  1. 最重要区别是的G1在回收阶段是多线程并发回收的,但必须得停止用户线程,而Shenandoah不需要停止用户线程!
  2. 维护跨代引用的方面,G1使用的是复杂的数据集,Shenandoah使用的是连接矩阵进行维护。
  3. Shenandoah目前还不支持分代模式,就是说Shenandoah无法指定一些region,不支持并不是否定分代思想,分代的设计一直很优秀,只是出于性价比权重的考虑,暂时没有去支持。

Shenandoah工作流程

Shenandoah的工作流程大致分为九个步骤,前面的步骤大多和G1相似

  1. 初始标记(Initial Marking),和G1一样,初始标记都是寻求Gc roots的根对象,需要stw,但寻求gcroots对象和堆大小的无关,所以很短暂。
  2. 并发标记(Concurrent Marking)和G1一样,找出所有gc root 可达的对象,并发标记是不需要stw的,标记的时长取决于堆内存活对象的数量。
  3. 最终标记(Final Marking)和G1一样,处理快照,确认之前已标记的对象是否有更改记录,将回收的垃圾按照成本和价值计算得到一个回收集,需要stw。
  4. 并发清理(Concurrent Cleanup)这是处理一个region内没有一个找到一个存活对象,全是待回收的垃圾,这种region称为Immediate Garbage Region。
  5. 并发回收(Concurrent Evacuation)并发回收这是Shenandoah与其他回收器最大的优点,并发回收的时候,Shenandoah首先要将回收集内存活的对象复制到一个未被使用的region中。冻结用户线程的话,复制对象是很简答的,但与用户线程并行执行,就变的复杂了。
    难点在与移动对象的同时,用户线程可能还不停的对象进行读写,移动对象是一次性行为,但移动之后所有内存对该对象的引用依然还是旧的引用地址,这不是一次性就能完全修改过来。Shenandoah对此采用了读屏障和Brooks Pointers的转发指针来解决问题并发回收的时长取决于回收集的大小。
  6. 初次引用更新(Initial Update Reference) 并发回收后,要对堆内对存活对象的引用从旧地址更新到最新的内存地址,这一操作称为引用更新。这个阶段其实并没有进行真正的引用更新,它只是设置了一个线程的集合点,确保所有并发回收阶段中收集器的线程正常完成对象的移动工作。初次引用更新的时间很短,也会有一个很短暂的stw。
  7. 并发引用更新(Concurrent Update Reference)真正进行引用更新的阶段,与用户线程并行执行,与并发标记不同,不需要根据gc roots遍历,只是单纯的跟句内存地址,线性找出引用类型,将其引用地址旧值变新值。
  8. 最终引用更新(Final Update Reference)解决了堆中的引用更新后,还需要修正gc roots的引用更新,需要stw。
  9. 并发清理(Concurrent Cleanup)经过并发回收和引用更新后,region内再无存活对象,全是待回收垃圾。
    如图介绍了 Shenandoah的工作流程,主要的三个并发阶段,并发标记,并发回收,并发更新引用
    在这里插入图片描述

转发指针

Shenandoah之所以做到移动对象的时候也支持用户线程原因就在于Brooks Pointers的转发指针。介绍转发指针之前,要做到移动对象时不停止用户线程就只能借助于内存保护陷阱。
内存保护陷阱的思想在需要移动的对象上设计内存陷阱,一旦用户访问对象访问到的是旧的内存地址空间,就自陷中段,进入了预设好的异常处理器中,再由异常处理器中对其代码进行转发并复制到新的对象上。实现内存保护陷阱是需要操作系统的支持,否则会导致频繁切换用户态和内核态,对资源消耗极大,所以不能频繁使用。

针对内存陷阱的缺点,Brooks 提出了转发指针的思想:在对象的内存布局上新增一个引用字段,没有移动对象的时候就指向自己,移动对象的时候,即指向新的地址,这样代码访问到旧对象的时候,通过访问引用字段就能正确找到新的内存地址。
转发指针有些类似于句柄池,不过句柄池是在堆内专门划分一个区域管理所有的句柄,而转发指针是在所有的对象内存布局上开辟一块区域。
直接观察可知这样就又额外的增加了内存的消耗,但相比内存陷阱来说,代价还是小很多,其实既然是转发指针要写向新的地址进行写操作,所以自然涉及到并发问题,所幸这方面使用了CAS的解决方案。

后话

本文着重介绍了一款低延迟的gc 回收器Shenandoah,了解了其工作流程以及转发指针解决移动对象支持用户线程并行的解决方案。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值