G1算法中的记忆集是做什么用的?
What is remembered set in G1 algorithms used for?
刚看了一些关于G1算法的博客
remembered-set 的用法让我很困惑。
这是我的想法:
既然我们可以使用 DFS 遍历 GC-Roots 中的每个引用,为什么我们需要 remembered-set?
导致所有的博客都说我们使用remembered-set的原因是我们不需要检查每个区域来查看是否有被GC-Roots引用的对象
你需要先了解 Card Table
是什么,IMO。如果有从 old generation
回到 young
的引用,你如何只扫描 young generation
区域并清理它?您需要 "track" 这些连接存在的确切位置 - 因此在扫描时 young generation
您可以在不破坏堆的情况下清理它。
想一想:你不能将一个对象 A
标记为删除,如果它现在在年轻一代中,如果有对它的引用 B
,来自 old generation
.但请记住,现在 - 您仅 在年轻的集合中。因此,为了跟踪这些 "connections",实施了 Card Table
。这张卡片的每一位 table 表示老年代的某个部分是 "dirty",意思是 在扫描年轻代的同时也扫描老年代的那部分 。
你为什么需要那个?扫描 young 的全部目的是扫描堆中的一小部分,而不是全部。 card table
实现了这一点。
G1
有区域。如果您正在扫描 regionA
并且发现它有指向其他 regionB
的指针怎么办?仅将此信息放在 Card Table
中是不够的。您的卡 table 只会知道 regionA
,而您下次扫描 regionB
- 您怎么知道您应该也扫描 regionA
?如果你不这样做,显然堆完整性被破坏了。
因此:remembered sets
。这些集合由一个异步线程填充:它扫描 card table
并根据该信息它还扫描这些 "dirty" 区域有指针的位置。它会跟踪 regionA -> regionB
连接。每个区域都有自己的 remembered set
。
所以当你到达GC需要发生的地步时,当扫描regionB
时你也查看它的remembered set
并发现你还需要扫描regionA
。
在实践中,这就是 G1
成为一代人的原因:这些 remembered sets
结果是 巨大 。如果你把堆分成 young
和 old
,就没有必要保持年轻代之间的联系,反正你一次扫描它们,从而消除了这些集合大小的消耗。 G1
想要保持 200ms
(默认)承诺 - 为此,您需要一次扫描年轻一代 (因为区域之间没有连接在 remembered sets
中,否则堆完整性消失了),但同时如果你让年轻一代变小 - remembered sets
的大小将会很大。
因此,触及这些设置是一个工程奇迹,恕我直言。
刚看了一些关于G1算法的博客
remembered-set 的用法让我很困惑。
这是我的想法:
既然我们可以使用 DFS 遍历 GC-Roots 中的每个引用,为什么我们需要 remembered-set?
导致所有的博客都说我们使用remembered-set的原因是我们不需要检查每个区域来查看是否有被GC-Roots引用的对象
你需要先了解 Card Table
是什么,IMO。如果有从 old generation
回到 young
的引用,你如何只扫描 young generation
区域并清理它?您需要 "track" 这些连接存在的确切位置 - 因此在扫描时 young generation
您可以在不破坏堆的情况下清理它。
想一想:你不能将一个对象 A
标记为删除,如果它现在在年轻一代中,如果有对它的引用 B
,来自 old generation
.但请记住,现在 - 您仅 在年轻的集合中。因此,为了跟踪这些 "connections",实施了 Card Table
。这张卡片的每一位 table 表示老年代的某个部分是 "dirty",意思是 在扫描年轻代的同时也扫描老年代的那部分 。
你为什么需要那个?扫描 young 的全部目的是扫描堆中的一小部分,而不是全部。 card table
实现了这一点。
G1
有区域。如果您正在扫描 regionA
并且发现它有指向其他 regionB
的指针怎么办?仅将此信息放在 Card Table
中是不够的。您的卡 table 只会知道 regionA
,而您下次扫描 regionB
- 您怎么知道您应该也扫描 regionA
?如果你不这样做,显然堆完整性被破坏了。
因此:remembered sets
。这些集合由一个异步线程填充:它扫描 card table
并根据该信息它还扫描这些 "dirty" 区域有指针的位置。它会跟踪 regionA -> regionB
连接。每个区域都有自己的 remembered set
。
所以当你到达GC需要发生的地步时,当扫描regionB
时你也查看它的remembered set
并发现你还需要扫描regionA
。
在实践中,这就是 G1
成为一代人的原因:这些 remembered sets
结果是 巨大 。如果你把堆分成 young
和 old
,就没有必要保持年轻代之间的联系,反正你一次扫描它们,从而消除了这些集合大小的消耗。 G1
想要保持 200ms
(默认)承诺 - 为此,您需要一次扫描年轻一代 (因为区域之间没有连接在 remembered sets
中,否则堆完整性消失了),但同时如果你让年轻一代变小 - remembered sets
的大小将会很大。
因此,触及这些设置是一个工程奇迹,恕我直言。