GC时如何处理对象引用

疑问:

在学习GC的时候发现,无论是 Mark and Copy,还是 Mark-Sweep-Compact 算法,都要移动对象,这必然会导致对象的内存地址发生变动,那么移动后,对象是怎么找到在堆中对象的新内存地址的?

难道每移动一个对象,就会找到并更新所有引用这个对象的 reference?

垃圾收集算法:https://plumbr.io/handbook/garbage-collection-algorithms

Sun HotSpot 对象的访问方式:

《深入理解Java虚拟机》第2版中,第2章的2.3.3 对象的访问定位中提到,对象访问方式目前主流有两种:

  • 句柄
  • 直接指针*

HotSpot 中是使用直接指针来定位对象的。

句柄:

如果使用句柄访问的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体信息。

使用句柄来访问的最大好处就是 reference 中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。

img

直接指针:

如果使用直接指针访问,那么 Java 对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象地址。

使用直接指针访问方式的最大好处就是速度快,它节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此之类开销积少成多后也是一项非常可观的指向成本。

就本书讨论的主要虚拟机 Sun HotSpot 而言,它就是使用第二种方式进行对象访问的,但从整个软件开发的范围来看,各种语言和框架使用句柄访问的情况也十分常见。

GC 时如何处理对象引用:

在 HotSpot 里 Java 对象的对象头存在两个字段(大小为2 word):

_mark  // mark word
_klass // klass pointer

mark word 用于存储多种信息,例如对象的 identity hash code、锁状态、full gc 时 mark 状态等等。

在 GC 时,如果一个对象被拷贝了,那么该对象头中 mark word 的 forwarding pointer 就会指向拷贝后的对象的地址。

Yong GC 类算法流程:

首先从 GC Roots 和 Old -> Young 的 Card Table(即存储了老年代对象与新生代对象之间的引用关系)出发,扫描追踪整个新生代的对象关系图。注意,在扫描过程中如果碰到指向老年代对象的引用,则停止这一路径的扫描。同时每扫描到一个对象,如果它是第一次被标记的话,我们就会将其拷贝到 survivor 区,或者晋升到老年代,并且在原对象位置的 mark word 域填上它的新地址 forwarding pointer。这样,如果原对象同时被两个以上的 reference 指向,那么在追踪过程中,别的 reference 还是有机会碰到此对象的原位置,然后发现它已经被标记过了,所以只需要通过 mark word 域的 forwarding pointer 更新 reference 值即可。

使用这类算法的有 Serial Young GC(即DefNew)、Parallel Young GC、ParNew,以及 G1 GC 的 Young GC & Mixed GC。

只需要一次遍历就可以完成对对象的拷贝和 reference 的更新。

Full GC 类算法流程:

有4个主要步骤:

1 标记:

  • 直接从 GC Roots 出发,扫面一遍整个堆(有时可以加上 metaspace),找到所有活的对象。

2 计算新地址:

  • 既然已知所有活的对象,那么就能够准确计算出它们在 compaction 后的新地址,然后将计算好的新地址保存到 mark word 域中。

3 更新 reference:

  • 更新所有活对像中指向其他对象的 reference 的值,让它们指向步骤 2 中计算好的新地址(从 mark word中读取)。

4 复制对象到新地址:

  • 将对象复制到步骤 2 计算的新地址。

使用这类算法的有 Serial Old GC、PS MarkSweep GC、Parallel Old GC、Full GC for CMS 和 Full GC for G1 GC。

参考:

[StackOverFlow] how a copying collector deals with this problem : https://stackoverflow.com/questions/9465767/if-the-jvm-keeps-moving-objects-around-when-it-does-gc-how-does-it-resolve-refe

[知乎] 关于Copy与Compaction两种算法如何处理对象地址: https://www.zhihu.com/question/42181722

[知乎] Copy GC的基本原理: https://zhuanlan.zhihu.com/p/28197216

[知乎] Mark & Compaction GC的基本原理: https://zhuanlan.zhihu.com/p/30797760

[掘金] JVM之卡表(Card Table): https://juejin.im/post/5c39920b6fb9a049e82bbf94

posted @ 2020-02-09 01:57  demo杰  阅读(986)  评论(0编辑  收藏  举报