Shenandoah 2.0 消除转发指针
Shenandoah 2.0 elimination of forwarding pointer
在 Shenandoah 1.0
中,每个 Object 都有一个额外的 header - 称为 forwarding pointer
。为什么需要这样做,导致它在 Shenandoah 2.0
中被淘汰的原因是什么?
首先,每个java Object有两个header:klass
和mark
。它们永远存在于每个实例中(例如,它们可以稍微改变 JVM 处理 their flags internally 与最近的 JVM 的方式)并且出于各种原因被使用(将仅详细介绍其中一个答案)。
forwarding pointer
字面意思是 the second part of this answer。 read barrier
和 Shenandoah 1.0
中的 write barrier
都需要 forwarding pointer
(尽管 read 可以跳过某些字段类型的障碍 -就不细说了)。用非常简单的话来说,它大大简化了 并发复制 。如该答案所述,它允许自动将 forwarding pointer
切换到 Object 的新副本,然后 同时 更新所有引用以指向新的 Object.
Shenandoah 2.0
中的情况发生了一些变化,其中“to-space 不变量”就位:这意味着所有的写入和读取都是通过 to-space
完成的。这意味着一个有趣的事情:一旦建立了 to-space
副本,就永远不会使用 from-copy
。想象一下这样的情况:
refA refB
| |
fwdPointer1 ---- fwdPointer2
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
在 Shenandoah 1.0
中,有些情况下 通过 refA
读取 可以绕过障碍(根本不使用它)和 仍然 阅读通过from-copy
。例如,final
字段允许这样做(通过特殊标志)。这意味着即使 to-space
副本已经存在并且已经有对它的引用,仍然会有 reads(通过 refA
)会转到 from-space
复制。 Shenandoah 2.0
这是被禁止的。
这些信息的使用方式非常有趣。 Java 中的每个 object 都对齐到 64 位 - 这意味着最后 3 位 总是 零。因此,他们删除了 forwarding pointer
并表示:如果 mark
字的最后两位是 11
(这是允许的,因为没有其他人以这种方式使用它)-> 这是forwarding pointer
,否则 to-space
副本还存在,这是一个普通的 header。你可以 see it in action right here and you can trace the masking here and here.
以前是这样的:
| -------------------|
| forwarding Pointer |
| -------------------|
| -------------------|
| mark |
| -------------------|
| -------------------|
| class |
| -------------------|
并转变为:
| -------------------|
| mark or forwarding | // depending on the last two bits
| -------------------|
| -------------------|
| class |
| -------------------|
所以这是一种可能的情况(为简单起见,我将跳过 class header
):
refA, refB
|
mark (last two bits are 00)
|
---------
| i = 0 |
| j = 0 |
---------
GC
开始。 refA/refB
引用的 object 是活的,因此必须撤离(据说在“collection 集合中”) .首先创建一个副本,并以原子方式 mark
引用该副本(最后两位标记为 11
现在使其成为 forwardee
而不是 mark word
):
refA, refB
|
mark (11) ------ mark (00)
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
现在 mark word
之一有一个位模式(以 11
结尾)表明它是 forwardee 而不是标记词再也没有了。
refA refB
| |
mark (11) ------ mark (00)
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
refB
可以并发移动,所以 refA
最终没有对 from-space
object 的引用,它是垃圾。如果需要,这就是 mark word
作为 forwarding pointer
的方式。
在 Shenandoah 1.0
中,每个 Object 都有一个额外的 header - 称为 forwarding pointer
。为什么需要这样做,导致它在 Shenandoah 2.0
中被淘汰的原因是什么?
首先,每个java Object有两个header:klass
和mark
。它们永远存在于每个实例中(例如,它们可以稍微改变 JVM 处理 their flags internally 与最近的 JVM 的方式)并且出于各种原因被使用(将仅详细介绍其中一个答案)。
forwarding pointer
字面意思是 the second part of this answer。 read barrier
和 Shenandoah 1.0
中的 write barrier
都需要 forwarding pointer
(尽管 read 可以跳过某些字段类型的障碍 -就不细说了)。用非常简单的话来说,它大大简化了 并发复制 。如该答案所述,它允许自动将 forwarding pointer
切换到 Object 的新副本,然后 同时 更新所有引用以指向新的 Object.
Shenandoah 2.0
中的情况发生了一些变化,其中“to-space 不变量”就位:这意味着所有的写入和读取都是通过 to-space
完成的。这意味着一个有趣的事情:一旦建立了 to-space
副本,就永远不会使用 from-copy
。想象一下这样的情况:
refA refB
| |
fwdPointer1 ---- fwdPointer2
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
在 Shenandoah 1.0
中,有些情况下 通过 refA
读取 可以绕过障碍(根本不使用它)和 仍然 阅读通过from-copy
。例如,final
字段允许这样做(通过特殊标志)。这意味着即使 to-space
副本已经存在并且已经有对它的引用,仍然会有 reads(通过 refA
)会转到 from-space
复制。 Shenandoah 2.0
这是被禁止的。
这些信息的使用方式非常有趣。 Java 中的每个 object 都对齐到 64 位 - 这意味着最后 3 位 总是 零。因此,他们删除了 forwarding pointer
并表示:如果 mark
字的最后两位是 11
(这是允许的,因为没有其他人以这种方式使用它)-> 这是forwarding pointer
,否则 to-space
副本还存在,这是一个普通的 header。你可以 see it in action right here and you can trace the masking here and here.
以前是这样的:
| -------------------|
| forwarding Pointer |
| -------------------|
| -------------------|
| mark |
| -------------------|
| -------------------|
| class |
| -------------------|
并转变为:
| -------------------|
| mark or forwarding | // depending on the last two bits
| -------------------|
| -------------------|
| class |
| -------------------|
所以这是一种可能的情况(为简单起见,我将跳过 class header
):
refA, refB
|
mark (last two bits are 00)
|
---------
| i = 0 |
| j = 0 |
---------
GC
开始。 refA/refB
引用的 object 是活的,因此必须撤离(据说在“collection 集合中”) .首先创建一个副本,并以原子方式 mark
引用该副本(最后两位标记为 11
现在使其成为 forwardee
而不是 mark word
):
refA, refB
|
mark (11) ------ mark (00)
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
现在 mark word
之一有一个位模式(以 11
结尾)表明它是 forwardee 而不是标记词再也没有了。
refA refB
| |
mark (11) ------ mark (00)
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
refB
可以并发移动,所以 refA
最终没有对 from-space
object 的引用,它是垃圾。如果需要,这就是 mark word
作为 forwarding pointer
的方式。