疑問:
在學習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