聊聊ShenandoahGC的Brooks Pointers
序
本文主要研究一下ShenandoahGC的Brooks Pointers
Shenandoah
Shenandoah面向low-pause-time的垃圾收集器,它的GC cycle主要有
-
Snapshot-at-the-beginning concurrent mark
包括Init Mark(Pause)、Concurrent Mark、Final Mark(Pause)
-
Concurrent evacuation(
這個階段用到了Brooks Pointers(
object version change with additional atomically changed indirection)進行copy
) -
Concurrent update references (optional)
包括Init update Refs(Pause)、Concurrent update Refs、Final update Refs(Pause)
其中Final Mark或者Final update Refs之後都可能進行Concurrent cleanup,進行垃圾回收,reclaims region
Brooks Pointers
G1 GC在evacuation階段是parallel的,但不是concurrent,ShenandoahGC為了做到concurrent copy使用了Brooks Pointers。
Rodney A. Brooks在<<Trading Data Space for Reduced Time and Code Space in Real-Time Garbage Collection on Stock Hardware>>這篇論文提出了一種使用forwarding pointer來做到concurrent copy的方案,該方案在所有物件的記憶體結構上新增一個forwarding pointer,它要麼指向物件自己,要麼指向在to-region的自己的拷貝
其要點如下:
- evacuation的第一步是拷貝from-region的物件到to-region
- evacuation的第二步使用CAS改變from-region的物件的forwarding pointer由自己變為指向to-region的拷貝物件
- evacuation的第三步就是遍歷heap,更新引用到to-region的拷貝物件
如果在evacuation期間,其他執行緒通過舊的引用訪問到了from-region的舊物件,它就需要根據舊物件的forwarding pointer找到to-region的拷貝物件;等所有舊物件的引用都更新完之後,後續就可以回收from-region的舊物件
示例程式碼
concurrent copy
class VersionUpdater<T, V> { final AtomicReference<T> ref = ...; void writeValue(V value) { do { T oldObj = ref.get(); T newObj = copy(oldObj); newObj.set(value); } while (!ref.compareAndSet(oldObj, newObj)); } }
這裡使用do while迴圈,即先拷貝再進行CAS,如果CAS不成功則繼續嘗試拷貝和CAS
write barriers
stub Write(val, obj, offset) { if (evac-in-progress && // in evacuation phase in-collection-set(obj) && // target is in from-space fwd-ptrs-to-self(obj)) { // no copy yet val copy = copy(obj); *(copy + offset) = val; // actual write if (CAS(fwd-ptr-addr(obj), obj, copy)) { return; // success! } } obj = fwd-ptr(obj); // write to actual copy *(obj + offset) = val; // actual write }
在evacuation階段,對from-region的物件的寫操作會觸發該物件的evacuation操作(如果該物件在to-region還沒有copy的話
)
evacuation
stub evacuate(obj) { if (in-collection-set(obj) && // target is in from-space fwd-ptrs-to-self(obj)) { // no copy yet copy = copy(obj); CAS(fwd-ptr-addr(obj), obj, copy); } }
evacuate先判斷該物件是否在from-region且在to-region還沒有copy,如果滿足條件則進行拷貝,然後CAS修改舊物件的forwarding pointer指向拷貝物件
小結
object version change with additional atomically changed indirection