目录
2、write_ref_field_pre/write_ref_field/write_ref_array/write_region
3、static_write_ref_array_pre / static_write_ref_array_post
3、inline_write_ref_field_pre/inline_write_ref_field/inline_write_region/inline_write_ref_array
4、mod_card_iterate /dirty_card_iterate /dirty_card_range_after_reset
上一篇《Hotspot 垃圾回收之CollectedHeap 源码解析》中讲解CollectedHeap的定义时提到了一个BarrierSet类,该类的具体用途没有注释说明,但是该属性的调用方很多,本篇博客就详细探讨这个神秘的BarrierSet的实现和使用。
一、BarrierSet
1、定义
BarrierSet表示一个数据读写动作的栅栏,跟高速缓存中用来在不同CPU之间同步数据的的Barrier(内存屏障)完全不同,BarrierSet的功能类似于一个拦截器,在读写动作实际作用于内存前执行某些前置或者后置动作,其定义在hotspot src/share/vm/memory/barrierSet.hpp中。BarrierSet是一个抽象类,其类继承关系如下:

BarrierSet定义了一个枚举Name来描述不同类型的子类,如下:

BarrierSet定义的属性就两个,如下:

BarrierSet定义了几个虚方法来描述BarrierSet子类支持的动作,如下:

方法名中的ref表示引用类型的数据,prim表示基本类型的数据,has_read_ref_barrier表示该BarrierSet是在读取引用类型数据时执行的,has_write_ref_barrier表示该BarrierSet是在写入引用类型数据时执行的,has_write_ref_pre_barrier表示该BarrierSet是在写入引用类型数据前预先执行的。
与之功能类似的还有如下虚方法:

即BarrierSet支持的读写数据除了对象字段属性还有数组,MemRegion类型的数据,其定义的方法也可以按照数据类型整体上分为三类:
- 读写对象属性类型的数据,如read_ref_field,read_prim_field,write_ref_field,write_prim_field
- 读写数组类型的数据,如read_ref_array,read_prim_array,write_ref_array_pre,write_ref_array_pre,write_prim_array,write_ref_array
- 读写MemRegion类型的数据,如read_region,write_region
BarrierSet定义的方法都是虚方法,重点关注其基于虚方法实现的内联方法和静态方法的实现。
2、write_ref_field_pre/write_ref_field/write_ref_array/write_region
因为BarrierSet的子类只有支持写屏障的类型,所以BarrierSet基于子类的虚方法提供了上述内联方法的默认实现,然后重点关注对应的子类虚方法实现即可。其实现如下:
template <class T> void BarrierSet::write_ref_field_pre(T* field, oop new_val) {
if (kind() == CardTableModRef) {
((CardTableModRefBS*)this)->inline_write_ref_field_pre(field, new_val);
} else {
write_ref_field_pre_work(field, new_val);
}
}
void BarrierSet::write_ref_field(void* field, oop new_val, bool release) {
if (kind() == CardTableModRef) {
((CardTableModRefBS*)this)->inline_write_ref_field(field, new_val, release);
} else {
write_ref_field_work(field, new_val, release);
}
}
//start是准备写入数组的起始元素的地址,count是写入的元素个数
void BarrierSet::write_ref_array(HeapWord* start, size_t count) {
assert(count <= (size_t)max_intx, "count too large");
HeapWord* end = (HeapWord*)((char*)start + (count*heapOopSize));
//在没有开启指针压缩的场景下,start和end都是经过对齐的,start等于aligned_start,end等于aligned_end
//开启指针压缩了则不一定
HeapWord* aligned_start = (HeapWord*)align_size_down((uintptr_t)start, HeapWordSize);
HeapWord* aligned_end = (HeapWord*)align_size_up ((uintptr_t)end, HeapWordSize);
// If compressed oops were not being used, these should already be aligned
assert(UseCompressedOops || (aligned_start == start && aligned_end == end),
"Expected heap word alignment of start and end");
write_ref_array_work(MemRegion(aligned_start, aligned_end));
}
void BarrierSet::write_region(MemRegion mr) {
if (kind() == CardTableModRef) {
((CardTableModRefBS*)this)->inline_write_region(mr);
} else {
write_region_work(mr);
}
}
上述四个方法的调用链如下:



上述调用链其实并不完整,还有一些典型场景如修改对象字段属性时触发的不在列表中,后面的博客会挑选典型的场景说明其应用。
3、static_write_ref_array_pre / static_write_ref_array_post
这两方法是BarrierSet对外的仅有的两个static方法,同样是基于虚方法实现,如下:
void BarrierSet::static_write_ref_array_pre(HeapWord* start, size_t count) {
assert(count <= (size_t)max_intx, "count too large");
if (UseCompressedOops) {
Universe::heap()->barrier_set()->write_ref_array_pre((narrowOop*)start, (int)count, false);
} else {
Universe::heap()->barrier_set()->write_ref_array_pre( (oop*)start, (int)count, false);
}
}
// count is number of array elements being written
void BarrierSet::static_write_ref_array_post(HeapWord* start, size_t count) {
// simply delegate to instance method
Universe::heap()->barrier_set()->write_ref_array(start, count);
}
这两方法的调用链一致,如下:


二、CardTableModRefBS
1、定义
ModRefBarrierSet继承自BarrierSet,表示一个只支持引用字段类型修改的BarrierSet,ModRefBarrierSet中用来描述其支持的动作类型的方法的实现如下:


ModRefBarrierSet在同目录的modRefBarrierSet.hpp中,ModRefBarrierSet提供了BarrierSet中定义的虚方法的空实现,同时新增了两个虚方法。
CardTableModRefBS继承自ModRefBarrierSet,表示一个在卡表(Card Table)下使用的ModRefBarrierSet,其补充的描述其支持的动作类型的方法的实现如下:

在CardTableModRefBS当前的实现下,当某个对象o的引用类型字段发生了修改,包含了对象o的对象头的卡表项被标记为脏的,而不是包含了这个被修改的字段的卡表项;当对象数组的某个元素发生修改了,只有包含了这个被修改元素的卡表项被标记为脏的。用来遍历查找脏的卡表项的MemRegionClosures的实现必须考虑上述问题。
卡表项的取值用枚举CardValues表示,其定义如下:

其中dirty_card和precleaned_card被认为是脏的,参考card_is_dirty_wrt_gen_iter方法的实现,如下:

每个卡表项对应的内存块card_size的大小通过枚举SomePublicConstants定义,如下:

深入解析Hotspot中BarrierSet的作用及其实现,重点介绍CardTableModRefBS类,包括其构造、核心方法实现及卡表项遍历机制。探讨其在垃圾回收过程中的关键角色。
最低0.47元/天 解锁文章
1462

被折叠的 条评论
为什么被折叠?



