Shenandoah 2.0消除了转发指针 [英] Shenandoah 2.0 elimination of forwarding pointer
问题描述
在Shenandoah 1.0
中,每个单个对象都有一个附加的标头-称为forwarding pointer
.为什么需要它?导致在Shenandoah 2.0
中将其消除的原因是什么?
In Shenandoah 1.0
every single Object had an additional header - called forwarding pointer
. Why was that needed and what is the reason that lead to its elimination in Shenandoah 2.0
?
推荐答案
首先,每个Java对象都有两个标头:klass
和mark
.从一开始它们就一直存在于每个实例中(它们可以稍微改变JVM处理
First of all, every single java Object has two headers: klass
and mark
. They have been there in each instance since forever (they can slightly change how a JVM handles their flags internally with recent JVMs, for example) and are used for various reasons (will go into detail about only one of them a bit further in the answer).
forwarding pointer
的需求实际上在答案的第二部分. forwarding pointer
和read barrier
中的read barrier
和write barrier
都需要forwarding pointer
原子切换到该对象的新副本,然后同时更新所有引用以指向该新对象.
The need for a forwarding pointer
is literally in the second part of this answer. The forwarding pointer
is needed in both read barrier
and write barrier
in Shenandoah 1.0
(though the read could skip the barrier for some field types - will not go into detail). In very simple words it simplifies concurrent copy very much. As said in that answer, it allows to atomically switch the forwarding pointer
to the new copy of the Object and then concurrently update all references to point to that new Object.
在Shenandoah 2.0
中,"to-space invariant"变为"to-space invariant".到位:意味着所有的写入和读取都通过to-space
完成.这意味着一件有趣的事情:一旦建立了to-space
副本,就不再使用from-copy
.想象这样的情况:
Things have changed a bit in Shenandoah 2.0
where the "to-space invariant" is in place : meaning all the writes and reads are done via the to-space
.This means one interesting thing : once the to-space
copy is established, the from-copy
is never used. Imagine a situation like this:
refA refB
| |
fwdPointer1 ---- fwdPointer2
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
在Shenandoah 1.0
中,有时通过refA
读取 会绕过障碍(根本不使用它),并且通过from-copy
读取 still .例如,这允许用于final
字段(通过特殊标志).这意味着,即使to-space
副本已经存在并且已经有对其的引用,仍可能会有 reads (通过refA
)(将通过refA
复制)到from-space
副本.在Shenandoah 2.0
中,这是禁止的.
In Shenandoah 1.0
there were cases when reading via the refA
could bypass the barrier (not use it at all) and still read via the from-copy
. This was allowed for final
fields, for example (via a special flag). This means that even if to-space
copy already existed and there were already references to it, there could still be reads (via refA
) that would go to the from-space
copy. In Shenandoah 2.0
this is prohibited.
此信息以相当有趣的方式使用. Java中的每个对象都按64位对齐-表示最后3位始终为 零.因此,他们放弃了forwarding pointer
并说:如果mark
字的最后两位是11
(这是允许的,因为没有其他方式以这种方式使用它)->这是forwarding pointer
,否则to-space
副本不存在,并且这是普通标头.您可以看到它就在这里,您可以跟踪此处.
This information was used in a rather interesting way. Every object in Java is aligned to 64 bits - meaning the last 3 bits are always zero. So, they dropped the forwarding pointer
and said that : if the last two bits of the mark
word are 11
(this is allowed since no else uses it in this manner) -> this is a forwarding pointer
, otherwise the to-space
copy does yet exists and this is a plain header. You can 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
):
So here is a possible scenario (I'll skip class header
for simplicity):
refA, refB
|
mark (last two bits are 00)
|
---------
| i = 0 |
| j = 0 |
---------
GC
启动.refA/refB
引用的对象是活动的,因此必须撤离(据说它在收集集"中).首先创建一个副本,并自动使用mark
引用该副本(最后两个位也标记为11
,现在将其设置为forwardee
而不是mark word
):
GC
kicks in. The object referenced by refA/refB
is alive, thus must be evacuated (it is said to be in the "collection set"). First a copy is created and atomically mark
is made to reference that copy (also the last two bits are marked as 11
to now make it a forwardee
and not a mark word
):
refA, refB
|
mark (11) ------ mark (00)
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
现在,其中一个mark word
具有一个位模式(以11
结尾),表明它是被转发者,而不再是标记词.
Now one of the mark word
s has a bit pattern (ends in 11
) that indicates that it is a forwardee and not a mark word anymore.
refA refB
| |
mark (11) ------ mark (00)
|
--------- ---------
| i = 0 | | i = 0 |
| j = 0 | | j = 0 |
--------- ---------
refB
可以同时移动,因此refA
最终不会引用from-space
对象,这是垃圾.如果需要,这就是mark word
充当forwarding pointer
的方式.
refB
can move concurrently, so then refA
, ultimately there are not references to the from-space
object and it is garbage. This is how mark word
acts as a forwarding pointer
, if needed.
这篇关于Shenandoah 2.0消除了转发指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!