Java Shenandoah 2.0消除转发指针

Java Shenandoah 2.0消除转发指针,java,garbage-collection,jvm,shenandoah,Java,Garbage Collection,Jvm,Shenandoah,在Shenandoah 1.0中,每个对象都有一个额外的头,称为转发指针。为什么需要这样做?是什么原因导致它在shenandoah2.0中被消除?首先,每个java对象都有两个头:klass和mark。它们一直存在于每个实例中(例如,它们可以稍微改变JVM处理最近的JVM的方式),并用于各种原因(在回答中只会进一步详细介绍其中一个) 需要一个转发指针。在Shenandoah 1.0中的读取屏障和写入屏障中都需要转发指针(尽管对于某些字段类型,读取可以跳过屏障-不会详细介绍)。简单地说,它大大简化

Shenandoah 1.0
中,每个对象都有一个额外的头,称为
转发指针
。为什么需要这样做?是什么原因导致它在
shenandoah2.0
中被消除?

首先,每个java对象都有两个头:
klass
mark
。它们一直存在于每个实例中(例如,它们可以稍微改变JVM处理最近的JVM的方式),并用于各种原因(在回答中只会进一步详细介绍其中一个)

需要一个
转发指针
。在
Shenandoah 1.0
中的
读取屏障
写入屏障
中都需要
转发指针
(尽管对于某些字段类型,读取可以跳过屏障-不会详细介绍)。简单地说,它大大简化了并发复制。正如回答中所说,它允许原子地将
转发指针
切换到对象的新副本,然后同时更新指向该新对象的所有引用

Shenandoah 2.0
中,“到空间不变量”已经就位:这意味着所有的写和读操作都是通过
到空间
完成的。这意味着一件有趣的事情:一旦
到空间
的拷贝建立起来,就永远不会使用
从拷贝
。想象一下这样的情况:

    refA            refB
      |               |
fwdPointer1 ---- fwdPointer2        
                      |
  ---------       ---------  
  | i = 0 |       | i = 0 | 
  | j = 0 |       | j = 0 | 
  ---------       ---------
| -------------------|
| forwarding Pointer |
| -------------------|

| -------------------|
|        mark        |
| -------------------|

| -------------------|
|        class       |
| -------------------|
Shenandoah 1.0
中,当通过
refA
读取时,可能会绕过障碍(根本不使用它),并且仍然通过
从副本读取。例如,
final
字段允许这样做(通过特殊标志)。这意味着,即使
到space
复制已经存在,并且已经有对它的引用,仍然可能有从space
复制到
的读取(通过
refA
)。在Shenandoah 2.0中,这是禁止的

这一信息的使用方式相当有趣。Java中的每个对象都与64位对齐,这意味着最后3位始终为零。因此,他们放弃了
转发指针
,并说:如果
标记
单词的最后两位是
11
(这是允许的,因为没有其他人以这种方式使用它)->这是一个
转发指针
,否则
到空格的副本仍然存在,这是一个普通标题。你可以,你可以追踪掩蔽和

它过去是这样的:

    refA            refB
      |               |
fwdPointer1 ---- fwdPointer2        
                      |
  ---------       ---------  
  | i = 0 |       | i = 0 | 
  | j = 0 |       | j = 0 | 
  ---------       ---------
| -------------------|
| forwarding Pointer |
| -------------------|

| -------------------|
|        mark        |
| -------------------|

| -------------------|
|        class       |
| -------------------|
并已转变为:

| -------------------|
| mark or forwarding |     // depending on the last two bits
| -------------------|

| -------------------|
|        class       |
| -------------------|
因此,这里有一个可能的场景(为了简单起见,我将跳过
类标题
):

GC
开始生效。
refA/refB
引用的对象处于活动状态,因此必须排空(据说它位于“集合集中”)。首先,创建一份副本,并以原子方式标记该副本(最后两位标记为
11
,现在将其标记为
转发人
,而不是
标记字
):

现在,
标记字
s中的一个有一个位模式(以
11
结尾),表示它是转发器,不再是标记字

       refA              refB            
         |                 |               
     mark (11) ------  mark (00)   
                           |
    ---------          ---------
    | i = 0 |          | i = 0 |
    | j = 0 |          | j = 0 |
    ---------          ---------
refB
可以同时移动,因此
refA
最终不会从空间中引用
对象,它是垃圾。这就是
标记单词
在需要时充当
转发指针的方式