共计 2710 个字符,预计需要花费 7 分钟才能阅读完成。
序
本文主要研究一下 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 指向拷贝对象
小结
Shenandoah 面向 low-pause-time 的垃圾收集器,它在 Concurrent evacuation 阶段用到了 Brooks Pointers(object version change with additional atomically changed indirection) 进行 copy,以实现 concurrent copy
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 的拷贝对象
doc
forwarding pointer
Shenandoah: A pauseless GC for OpenJDK
Shenandoah GC: An overview
Shenandoah GC: Concurrent parallel marking
Shenandoah GC: Brooks pointers
devoxx-Nov2017-shenandoah(示例代码来源于此 pdf)