前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shenandoah GC算法

Shenandoah GC算法

原创
作者头像
johnny666
发布2024-10-04 11:19:06
760
发布2024-10-04 11:19:06
举报
文章被收录于专栏:Java

概述

最早由Red Hat公司发起,目标是利用现代多核CPU的优势,减少大堆内存在GC时产生的停顿时间。随OpenJDK 12一起发布,暂停时间不依赖于堆的大小;这意味着无论堆的大小如何,暂停时间都是差不多的。

Shenandoah最初的目标是把GC停顿时间降到毫秒级,并且将对内存的支持扩展到太字节级别。为降低停顿时间,回收器需要使用更多的线程来并发处理回收任务,而要在降低停顿时间的同时能够支持更大的堆空间,回收器对CPU的多核处理能力提出更高的要求。

支持的特性:

  • 解释器
  • C1屏障
  • C2屏障
  • 引用
  • JNI临界区域
  • System.gc()

策略

Shenandoah GC策略

策略

GC触发的条件

GC选择回收分区的条件

static

当内存可用达到一定的國值之后,将启动GC

在进行GC时,只选择垃圾超过一定阈值的分区

passive

直到发生OOM,直接启动FGC

在进行GC时,只选择垃圾超过一定阈值的分区,并且分区在转移后仍然能保留一定的预留空间

aggressive

总是启动GC

在进行GC时,只要分区有垃圾就会启动GC。判断分区是否有垃圾的条件:分区中垃圾超过一定的阈值;分区中可用的对象所占的空间小于一定的阈值

adaptive

默认选项。当内存可用达到一定的阈值之后,或从上次GC到现在已经使用的内存超过一定阈值后,或根据使用的内存预测可用的内存不足以支撑到下一次GC时,将启动GC

在进行GC时,只选择垃圾超过一定阈值的分区,并且分区在转移后仍然能保留一定的预留空间

compact

当内存可用达到一定的阈值之后,或从上次GC到现在已经使用的内存超过一定阈值后,将启动GC

在进行GC时,只选择垃圾超过一定阈值的分区,并且分区在转移后仍然能保留一定的预留空间。与自适应策略相比,它的阈值更低,回收的分区更多。

traversal

启动遍历GC,是正常GC的优化版,把重定向和标记进行合并。GC触发时机和adaptive策略相同

回收的分区策略也和adaptive策略相同

GC策略决定Shenandoah何时启动GC以及在GC时回收哪些分区。

算法

Shenandoah为了更好地管理内存,实现4类GC算法:

  1. 正常回收算法(Normal GC):GC的过程通常按照初始标记、并发标记、再标记、并发转移、结束转移的步骤执行
  2. 降级回收算法(Degenerated GC):在GC过程中,如果遇到内存分配失败,将进入降级回收。降级回收实质上是在STW中进行的并行回收
  3. 全回收算法(Full GC):如果在降级回收中再次遇到内存分配失败的情况,将进入全回收。和G1的并行FGC非常类似。
  4. 遍历回收算法(Traversal GC):GC过程按照初始遍历、并发遍历、预清理和结束遍历的步骤执行。

降级回收算法和并行FGC类似,都是在STW中进行的。它和FGC最大的区别就是降级回收会重用当前已经并发执行的标记、转移或者重定位的信息,从当前GC失败的地方继续向下执行。

正常回收

Shenandoah中正常回收有两种模式:一般模式和优化模式。区别在于是否在标记时执行重定位,在标记的过程中执行重定位,则称为优化模式,否则称为一般模式。可通过参数ShenandoahUpdateRefsEarly控制,可能值为off/false,表示GC执行优化模式;on/true/adaptive,表示执行一般模式。

一般模式GC步骤:

  • 初始标记:Init Mark,从根集合出发,标记根集合所有引用的对象,这些对象作为第二步并发标记的出发点。会触发STW
  • 并发标记:Concurrent Marking,以第一步标记的对象作为出发点,开始并发地标记对象
  • 预清理:在进入再标记阶段之前,先处理引用对象,把仍然活跃的引用对象重新激活,不进行真正的GC。在该阶段是并发执行的,但是只有一个并发工作线程执行预清理
  • 再标记:在该阶段要做3件事:终止标记、计算回收集、转移根集合直接的引用对象。会触发STW
  • 清理:再标记结束后,部分分区可能已经没有任何活跃对象,这些分区就可以被回收
  • 并发转移:Concurrent Evacuation,根据转移集,对所有在转移集中的活跃对象进行转移
  • 初始重定位:Init Update Refs,初始重定位将根据SATB算法重置分区中对象分配的起始内存地址位置。会触发STW
  • 并发重定位:遍历不属于回收集合中的分区的对象,根据brook pointer更新对象的引用指针
  • 结束重定位:遍历根集合中所有引用的对象,更新对象的引用指针。会触发STW
  • 再清理:因回收集合中对象全部转移完成,所以可以释放空间。

整个GC活动图:

优化模式GC的步骤:

  • 初始标记:和一般模式中初始标记相同
  • 并发标记:以第一步标记的对象作为出发点,开始并发地标记对象,注意在这一步中首先判断对象是否需要重定位,如果需要,则进行重定位
  • 预清理:和一般模式中预清理相同
  • 再标记:在该阶段主要做4件事,分别为更新根集合中所有对象的引用、终止标记、计算回收集、转移根集合直接的引用对象。在STW中进行
  • 清理:和一般模式中清理相同
  • 并发转移:和一般模式中并发转移相同
  • 结束转移:设置转移结束标记,重置TLAB等信息。在STW中进行

Shenandoah中的优化模式和ZGC的GC过程基本类似,把标记阶段和重定位阶段合并。正常回收在运行的过程中,应用程序和GC线程都可能需要分配内存空间,也都有可能遇到内存不足导致分配失败的情况,此时正常回收将进入降级回收状态,如果在降级回收时再遇到内存不足,将进入FGC状态。

正常回收、降级回收和FGC交互图:

遍历回收

Shenandoah遍历回收就是把并发标记、并发转移和并发重定位合并到一个阶段。

Shenandoah遍历回收的步骤:

  • 初始遍历:初始遍历待回收集合,从根集合出发,标记根集合所用引用的对象,这些对象作为第二步并发标记的出发点。会触发STW
  • 并发遍历:根据第一步标记的对象作为出发点,开始并发地标记对象
  • 结束遍历:该阶段主要做两件事,分别为标记SATB队列中新增的引用对象、终止标记。会触发STW
  • 清理:清理可回收的分区

遍历回收算法,思路简单但实现复杂。需要解决两个问题:

  1. 如何选择带回收的分区?在进入优化回收时,第一次GC还没有足够的信息表明分区对象的存活情况,所以需要在下一次GC时才能选择哪些分区能够被回收。
  2. 如何保持一致性?主要有3种一致性问题:遍历一致性、数据一致性和重定位一致性。
    • 遍历一致性:如何在遍历时正确地处理对象关系图的变化?
    • 数据一致性:如何保证读的时候总是访问最新的数据?如何保证写的时候能访问到正确的对象?
    • 重定位一致性:如何避免一般的成员变量更新和重定位成员变量更新的竞争?

Shenandoah中解决一致性问题的方法到也比较简单。通过执行顺序的不同来保证一致性。具体来说在标记时,如果对象需要更新引用,则先更新引用,如果没有引用更新,则判断是否需要转移,如果需要转移则转移,最后才能标记对象。

延伸

对比G1、ZGC

G1、ZGC和Shenandoah的异同点

对比项

G1

ZGC

Shenandoah GC

内存连续性

堆内存基于分区实现,最小分区为1MB,最大分区可达32MB

基于分页设计,类似于G1分区。页面有3种类型,2MB、32MB和N*MB

基于分区理论实现,最小分区为256KB,最大分区可为32MB

是否支持分代

支持

不支持

不支持

是否全量回收

新生代分区在 YGC/MixedGC/FGC 中会被全部回收老生代在 Mixed GC 时部分回收,在 FGC 时全部回收

部分页面回收

部分分区回收

屏障

支持读屏障和写屏障,读屏障是为了正确标记对象,写屏障是为了处理代际引用关系管理(G1中有单独的线程处理代际引用)

仅需要支持读屏障即可实现并发标记、并发转移和并发重定位

支持读屏障和写屏障,读屏障是为了对象正确标记和并发处理读数据写屏障是为了并发处理同时写数据、实际上Shenandoah还需要支持比较屏障(比较屏障是通过两个读屏障实现的)

GC时并发性

目前只支持并发标记

支持并发标记、并发转移和并发重定位

支持并发标记、并发转移和并发重定位

GC触发策略

使用衰减停顿模型预测是否启动GC

提供多种触发GC的规则,其中最为常见的触发方式是根据正态分布预测的内存分配的情况触发

提供多种触发GC的策略:static、passive、aggressive、compactadaptive

NUMA支持

尚未支持,提案JEP 345为G1支持NUMA

支持

不支持

字符串去重

支持

不支持

支持

引用处理

并行处理

并发处理

并行处理

在GC时非常类似,都实现并发垃圾标记、并发转移、并发重定位、压缩堆空间。Shenandoah更加类似于G1,把G1中的并行转移变成并发转移。

ZGC并发的基础是地址多视图映射。

Shenandoah并发的基础是在对象头增加一个额外数据Brook pointer,在实现读写屏障时通过Brook pointer访问对象。

参考

  • 新一代垃圾回收器ZGC设计与实现

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
    • 策略
    • 算法
      • 正常回收
        • 遍历回收
        • 延伸
          • 对比G1、ZGC
          • 参考
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档