1. 简介
对于并发GC来说,最复杂的事情在于GC worker在标记-整理,而Java线程(Mutator)同时还在不断的创建新对象、修改字段,不停的更新对象引用关系。因此并发GC一般采用两种策略Incremental Update(增量更新、CMS) 和SATB(snapshot at beginning、G1) ,两种策略网上介绍文章很多,此处不再赘述。
SATB重点关注引用关系的删除,可以参考我之前的博客JVM G1 源码分析(四)- Dirty Card Queue Set,而Incremental Update重点关注引用关系的增加。
而ZGC并没有采取类似方式,而是借助读屏障、colored pointer来实现并发标记-整理。
2. 原理
2.1 什么是Load Barrier
- 一小段在最佳位置由JIT注入的代码
- 从堆中加载一个对象引用时
- 检查这个引用是否是bad color
- 如果是,则自愈
2.2 Load Barrier的触发
从堆中加载对象引用时触发load barrier。
// 从堆中加载一个对象引用,需要load barrier
String n = person.name;
// 不需要load barrier,不是从堆中加载
String p = n;
// 不需要load barrier,不是从堆中加载
n.isEmpty();
// 不需要load barrier,不是引用类型
int age = person.age;
当引用类型n被赋值修改后,在下一次使用n前,会测试n的染色指针是否为good。此时测试为bad color可知n的引用地址进行过修改,需要自愈。
触发load barrier的伪代码如下:
// 从堆中加载一个对象引用,需要load barrier
String n = person.name;
if (n & bad_bit_mask) {
slow_path(register_for(n), address_of)
}
对应的汇编代码:
// String n = person.name;
mov 0x10(%rax), %rbx
// 是否bad color
test %rbx, (0x16)%r15
// 如是,进入slow path
jnz slow_path
2. 源码分析
2.1 掩码
zGlobals.hpp
//
// Good/Bad mask states
// --------------------
//
// GoodMask BadMask WeakGoodMask WeakBadMask
// --------------------------------------------------------------
// Marked0 001 110 101 010
// Marked1 010 101 110 001
// Remapped 100 011 100 011
//
// Good/bad masks
extern uintptr_t ZAddressGoodMask;
extern uintptr_t ZAddressBadMask;
extern uintptr_t ZAddressWeakBadMask;
zAddress.inline.hpp
inline bool ZAddress::is_null(uintptr_t value) {
return value == 0;
}
inline bool ZAddress::is_bad(uintptr_t value) {
return value & ZAddressBadMask;
}
inline bool ZAddress::is_good(uintptr_t value) {
return !is_bad(value) && !is_null(value);
}
从以上两段代码可以很清晰看出,colored pointer的状态是Good/WeakGood/Bad/WeakBad由GoodMask及BadMask来测定。
同时,GoodMask、BadMask由GC所处的阶段决定。
void ZAddress::set_good_mask(uintptr_t mask) {
ZAddressGoodMask = mask;
ZAddressBadMask = ZAddressGoodMask ^ ZAddressMetadataMask;
ZAddressWeakBadMask = (ZAddressGoodMask | ZAddressMetadataRemapped | ZAddressMetadataFinalizable) ^ ZAddressMetadataMask;
}
void ZAddress::initialize() {
ZAddressOffsetBits = ZPlatformAddressOffsetBits();
ZAddressOffsetMask = (((uintptr_t)1 << ZAddressOffsetBits) - 1) << ZAddressOffsetShift;
ZAddressOffsetMax = (uintptr_t)1 << ZAddressOffsetBits;
ZAddressMetadataShift = ZPlatformAddressMetadataShift();
ZAddressMetadataMask = (