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 本身不需要修改。
直接指针:
如果使用直接指针访问,那么 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