JVM学习笔记22

145 阅读3分钟

GC算法之G1-PART VII

Collection Set

在任何垃圾收集暂停期间,CSet 中的所有区域都被释放。 CSet 是一组区域,目标是在垃圾收集暂停期间进行回收。这些候选区域中的所有活动对象将在收集期间被疏散,并且这些区域将返回到空闲区域列表。

在新生代收集期间,CSet 只能包含用于收集的新生代区域。

另一方面,混合收集不仅会添加所有年轻代区域,还会将一些老年代候选区域(基于它们的 GC 效率)添加到其 CSet。

有两个重要参数有助于为混合集合的 CSet 选择候选的老年代区域

-XX:G1MixedGCLiveThresholdPercent

任何低于此活性阈值的老年代区域都包含在混合集合的 CSet 中。

-XX:G1OldCSetRegionThresholdPercent

设置每次混合收集暂停可以收集的旧区域数量的最大限制。该阈值取决于 JVM 进程可用的总 Java 堆大小,并表示为总 Java 堆的百分比。

Remembered Set

RSet 是一种数据结构,可帮助维护和跟踪对其自身区域的引用,从而无需扫描整个堆以获取此类信息。

当 G1 GC 执行停止世界收集(年轻或混合)时,它会扫描其 CSet 中包含的区域的 RSets。一旦区域中的活动对象被移动,它们的传入引用就会更新。

在任何年轻代或混合回收期间,总是会完整地收集年轻代,从而无需跟踪包含对象驻留在年轻代中的引用(个人理解:无需跟踪年轻代对老年代的引用)。这减少了 RSet 开销。

每个区域只有一个 RSet。

某些特定区域(以及它的 RSet)可能更新很频繁,这样在同一区域甚至同一位置可能会有许多更新。G1 GC 有自己的方式来处理这种不同区域更新频繁度的差异;它通过**改变 RSet 的密度(density)**来实现。

RSets 的密度遵循三个级别的粒度,即稀疏(sparse)、精细(fine)和粗糙(coarse)。

对于一个受欢迎的区域,RSet 可能会变得粗化以适应来自其他各个区域的指针。

三个粒度级别中的每一个都有一个针对任何特定 RSet 的区域表 (PRT:per-region-table) 。

由于 G1 GC 区域(region)在内部进一步划分为块,在 G1 GC 区域级别,可实现的最低粒度是一个 512 字节的堆块,称为“卡片(card)”。全局卡表card table)维护所有卡片。

当指针引用 RSet 的拥有区域时,包含该指针的卡片会在 (个人标注:卡片会在被引用区域的PRT中注明)PRT 中注明。

稀疏 PRT 基本上是这些卡片索引的哈希表。这个简单的实现导致垃圾收集器的扫描时间更快。

细粒度 PRT和粗粒度PRT的处理方式不同。

对于细粒度的 PRT,其开放哈希表中的每个条目对应到一个区域(带有对拥有区域的引用),该区域中的卡片索引存储在位图中。

细粒度 PRT 有一个最大限制,当超过它时,会在粗粒度位图中设置一个位(称为“粗粒度位”)。一旦设置了粗粒度位,就会删除细粒度 PRT 中的相应条目。

粗粒度PRT只是一个每个区域一位的位图,这样一个设置位意味着相应的区域可能包含对拥有区域的引用(个人注解:意味着该位图不包含卡片索引)。因此必须扫描与设置位相关联的整个区域以找到引用。因此,粗化为粗粒度位图的RSet是垃圾收集器扫描最慢的。

在任何收集周期中,当扫描RSet并因此扫描 PRT 中的卡片时,G1 GC 将在全局卡片表中标记相应的条目以避免重新扫描该卡片。在收集周期结束时,该卡表被清除。