目录
4、prepare_for_younger_refs_iterate /younger_refs_iterate /younger_refs_in_space_iterate
5、write_ref_field_gc /write_ref_field_gc_work /write_ref_field_gc_par
6、clear_into_younger / invalidate_or_clear
1、CollectedHeap ensure_parsability和new_store_pre_barrier
本篇博客继续上一篇《Hotspot 垃圾回收之BarrierSet(二) 源码解析》讲解BarrierSet的具体应用场景,从调用源头理解该类的用途。
一、CardTableRS
1、定义
CardTableRS继承自GenRemSet,Gen就是表示分代内存的Generation的缩写,Rem是Remembered的缩写,GenRemSet支持对跨代引用的遍历,尤其是老年代对象持有的对年轻代对象的引用。GenRemSet的定义在同目录的genRemSet.hpp中,其类继承关系如下:
GenRemSet同样定义了一个枚举Name来描述子类,如下:
定义的属性就两个,如下:
GenRemSet定义的方法都是未提供默认实现的虚方法,具体实现参考唯一的子类CardTableRS,CardTableRS借助CardTable来实现跨代引用的遍历。CardTableRS在GenRemSet的基础上增加了如下属性:
- CardTableModRefBSForCTRS* _ct_bs; //卡表的实现类
- jbyte* _last_cur_val_in_gen; //一个数组,用来记录所有的Generation的状态
- jbyte _cur_youngergen_card_val; //引用遍历时使用,表示当前遍历的有效card val
- int _regions_to_iterate; //记录_last_cur_val_in_gen中有效元素的个数
CardTableRS为了支持引用遍历,增加了一些扩展的CardValue,其定义如下:
整体上CardTableRS只是一层皮而已,核心方法的实现都在相关的子类中,后续会通过其他相关类的学习来反过来理解CardTableRS中的方法。
2、KlassRemSet
KlassRemSet是一个工具类,用来遍历所有Klass将其_accumulated_modified_oops标识置为0。Klass本身保存了一个属于这个类的java.lang.Class实例的oop,因为Klass是保存在Metaspace元空间中,不是在堆里面,所以当这个oop发生修改了,无法通过Card Table标记Klass的oop发生了修改,于是在Klass中添加了两个jbyte属性来标记Klass的oop发生了修改,如下:
_modified_oops是相当于Card Table中的打标,_accumulated_modified_oops相当于Mod Union Table中的打标。Klass配套定义了6个方法来读写这两个属性,如下:
从其实现可知只有_modified_oops为1的时候,才能把_accumulated_modified_oops置为1,查看record_modified_oops方法的调用链如下:
即当设置mirro oop的时候或者部分遍历oop的场景下会将_modified_oops置为1。
KlassRemSet的定义很简单,如下:
mod_union_is_clear方法用于判断是否存在_accumulated_modified_oops属性为1的Klass,clear_mod_union方法用于将所有Klass的 _accumulated_modified_oops属性置为0,这两方法的实现如下:
class HasAccumulatedModifiedOopsClosure : public KlassClosure {
bool _found;
public:
HasAccumulatedModifiedOopsClosure() : _found(false) {}
void do_klass(Klass* klass) {
//只要找到一个has_accumulated_modified_oops为true的_found就为true
if (_found) {
return;
}
if (klass->has_accumulated_modified_oops()) {
_found = true;
}
}
bool found() {
return _found;
}
};
bool KlassRemSet::mod_union_is_clear() {
HasAccumulatedModifiedOopsClosure closure;
//遍历所有的Klass
ClassLoaderDataGraph::classes_do(&closure);
return !closure.found();
}
class ClearKlassModUnionClosure : public KlassClosure {
public:
void do_klass(Klass* klass) {
if (klass->has_accumulated_modified_oops()) {
//清除_accumulated_modified_oops标识
klass->clear_accumulated_modified_oops();
}
}
};
void KlassRemSet::clear_mod_union() {
ClearKlassModUnionClosure closure;
//遍历所有的Klass
ClassLoaderDataGraph::classes_do(&closure);
}
上述的KlassClosure继承自Closure表示遍历Klass时对Klass执行的某个动作,其定义如下:
跟KlassClosure类似的还有多个,参考Closure的类继承关系,如下图:
3、构造和析构函数
两者的源码如下:
CardTableRS::CardTableRS(MemRegion whole_heap,
int max_covered_regions) :
GenRemSet(),
_cur_youngergen_card_val(youngergenP1_card), //youngergenP1_card是一个枚举值
_regions_to_iterate(max_covered_regions - 1)
{
if (UseG1GC) {
_ct_bs = new G1SATBCardTableLoggingModRefBS(whole_heap,
max_covered_regions);
} else {
_ct_bs = new CardTableModRefBSForCTRS(whole_heap, max_covered_regions);
}
_ct_bs->initialize();
set_bs(_ct_bs);
//NEW_C_HEAP_ARRAY3用于创建指定类型的数组,jbyte表示数组类型,max_gens是一个枚举值,值为10
//mtGC也是枚举MemoryType,CURRENT_PC返回一个NativeCallStack实例,用于回调的
_last_cur_val_in_gen = NEW_C_HEAP_ARRAY3(jbyte, GenCollectedHeap::max_gens + 1,
mtGC, CURRENT_PC, AllocFailStrategy::RETURN_NULL);
if (_last_cur_val_in_gen == NULL) {
//分配失败抛出异常
vm_exit_during_initialization("Could not create last_cur_val_in_gen array.");
}
for (int i = 0; i < GenCollectedHeap::max_gens + 1; i++) {
//设置数组的初始值
_last_cur_val_in_gen[i] = clean_card_val();
}
_ct_bs->set_CTRS(this);
}
CardTableRS::~CardTableRS() {
if (_ct_bs) {
//释放CardTableModRefBSForCTRS
delete _ct_bs;
_ct_bs = NULL;
}
if (_last_cur_val_in_gen) {
//释放数组
FREE_C_HEAP_ARRAY(jbyte, _last_cur_val_in_gen, mtInternal);
}
}
#define CURRENT_PC ((MemTracker::tracking_level() == NMT_detail && NMT_stack_walkable) ? \
NativeCallStack(0, true) : NativeCallStack::empty_stack())
#define NEW_C_HEAP_ARRAY3(type, size, memflags, pc, allocfail)\
(type*) AllocateHeap((size) * sizeof(type), memflags, pc, allocfail)
static jbyte clean_card_val() {
return CardTableModRefBS::clean_card;
}
CardTableRS在JVM进程存活期间不会被销毁,其析构函数没有调用方,构造函数的调用链如下:
表示ParallelGC算法的ParallelScavengeHeap不在调用链中,说明其没有直接使用CardTableRS。
4、prepare_for_younger_refs_iterate /younger_refs_iterate /younger_refs_in_space_iterate
prepare_for_younger_refs_iterate用于为年轻代引用遍历执行一些准备工作,younger_refs_iterate和younger_refs_in_space_iterate都是执行引用遍历,不同的遍历的范围不一样,一个是Space,一个是Generation。这三个方法的实现如下:
void CardTableRS::prepare_for_younger_refs_iterate(bool parallel) {
// 并行模式
if (parallel) {
// Find a parallel value to be used next.
jbyte next_val = find_unused_youngergenP_card_value();
set_cur_youngergen_card_val(next_val);
} else {
// 串行模式
set_cur_youngergen_card_val(youngergen_card);
}
}
void CardTableRS::younger_refs_iterate(Generation* g,
OopsInGenClosure* blk) {
_last_cur_val_in_gen[g->level()+1] = cur_youngergen_card_val();
g->younger_refs_iterate(blk);
}
void CardTableRS::younger_refs_in_space_iterate(Space* sp,
OopsInGenClosure* cl) {
const MemRegion urasm = sp->used_region_at_save_marks();
//遍历MemRegion内存区域对应的脏的卡表项并做处理
_ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm, cl, this);
}
MemRegion used_region_at_save_marks() const {
return MemRegion(bottom(), saved_mark_word());
}
jbyte CardTableRS::find_unused_youngergenP_card_value() {
for (jbyte v = youngergenP1_card;
v < cur_youngergen_and_prev_nonclean_card;
v++) {
bool seen = false;
//遍历_last_cur_val_in_gen,找到一个未使用的card_value
for (int g = 0; g < _regions_to_iterate; g++) {
if (_last_cur_val_in_gen[g] == v) {
seen = true;
break;
}
}
if (!seen) return v;
}
ShouldNotReachHere();
return 0;
}
void set_cur_youngergen_card_val(jbyte v) {
_cur_youngergen_card_val = v;
}
void CardTableModRefBS::non_clean_card_iterate_possibly_parallel(Space* sp,
MemRegion mr,
OopsInGenClosure* cl,
CardTableRS* ct) {
if (!mr.is_empty()) {
//如果mr非空
int n_threads = SharedHeap::heap()->n_par_threads();
bool is_par = n_threads > 0;
if (is_par) {
#if INCLUDE_ALL_GCS
assert(SharedHeap::heap()->n_par_threads() ==
SharedHeap::heap()->workers()->active_workers(), "Mismatch");
//并行遍历脏的卡表项
non_clean_card_iterate_parallel_work(sp, mr, cl, ct, n_threads);
#else // INCLUDE_ALL_GCS
fatal("Parallel gc not supported here.");
#endif // INCLUDE_ALL_GCS
} else {
//单线程遍历
DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(),
cl->gen_boundary());
ClearNoncleanCardWrapper clear_cl(dcto_cl, ct);
clear_cl.do_MemRegion(mr);
}
}
}
Generation,Space,OopsInGenClosure都有诸多个子类,这里暂且不讨论了。注意执行此younger_refs_iterate会更新_last_cur_val_in_gen的元素值。这三者的调用链如下:
从调用链可知这三者是配合使用的,prepare_for_younger_refs_iterate先执行,然后调用younger_refs_iterate,这个方法会最终调用younger_refs_in_space_iterate。
5、write_ref_field_gc /write_ref_field_gc_work /write_ref_field_gc_par
write_ref_field_gc_work是write_ref_field_gc的实现类,相当于一个回调方法,当垃圾回收器将新值写入某个属性时会回调此方法通知CardTableRS。write_ref_field_gc_par功能与write_ref_field_gc一样,不过是适用于并行的垃圾回收器。write_ref_field_gc方法的实现在同目录的genRemSet.inline.hpp中,如下:
这个方法的实现比较奇怪,kind方法并不是GenRemSet定义的方法,在if为true时调用的是inline_write_ref_field_gc方法。上述三个方法的实现如下:
void inline_write_ref_field_gc(void* field, oop new_val) {
//找到对应的卡表项,将其置为youngergen_card
jbyte* byte = _ct_bs->byte_for(field);
*byte = youngergen_card;
}
void write_ref_field_gc_work(void* field, oop new_val) {
inline_write_ref_field_gc(field, new_val);
}
void CardTableRS::write_ref_field_gc_par(void* field, oop new_val) {
jbyte* entry = ct_bs()->byte_for(field);
do {
jbyte entry_val = *entry;
//当前线程最先执行此方法,卡表项还是clean的,将其置为_cur_youngergen_card_val
if (entry_val == clean_card_val()) {
// No threat of contention with cleaning threads.
*entry = cur_youngergen_card_val();
return;
} else if (card_is_dirty_wrt_gen_iter(entry_val)
|| is_prev_youngergen_card_val(entry_val)) {
// 如果已经被其他线程处理过了,则将其置为cur_youngergen_and_prev_nonclean_card,如果修改失败则重试
jbyte new_val = cur_youngergen_and_prev_nonclean_card;
jbyte res = Atomic::cmpxchg(new_val, entry, entry_val);
// Did the CAS succeed?
if (res == entry_val) return;
// Otherwise, retry, to see the new value.
continue;
} else {
assert(entry_val == cur_youngergen_and_prev_nonclean_card
|| entry_val == cur_youngergen_card_val(),
"should be only possibilities.");
return;
}
} while (true);
}
jbyte cur_youngergen_card_val() {
return _cur_youngergen_card_val;
}
static bool
card_is_dirty_wrt_gen_iter(jbyte cv) {
return CardTableModRefBS::card_is_dirty_wrt_gen_iter(cv);
}
static bool card_is_dirty_wrt_gen_iter(jbyte cv) {
return cv == dirty_card || cv == precleaned_card;
}
//值在youngergen_card和cur_youngergen_and_prev_nonclean_card之间,即可能是中间的三个 (parallel) youngergen card values
bool is_prev_youngergen_card_val(jbyte v) {
return
youngergen_card <= v &&
v < cur_youngergen_and_prev_nonclean_card &&
v != _cur_youngergen_card_val;
}
6、clear_into_younger / invalidate_or_clear
clear_into_younger用于通知CardTableRS已经没有任何引用指向该Generation了,可以将该Generation对应的卡表项给置为clear了。invalidate_or_clear用于通知CardTableRS指定的Generation中的引用发生改变了,可能包含指向年轻代的引用,需要将某一部分置为dirty。其实现如下:
void CardTableRS::clear_into_younger(Generation* old_gen) {
assert(old_gen->level() == 1, "Should only be called for the old generation");
clear(old_gen->prev_used_region());
}
void clear(MemRegion mr) { _ct_bs->clear(mr); }
void CardTableRS::invalidate_or_clear(Generation* old_gen) {
assert(old_gen->level() == 1, "Should only be called for the old generation");
MemRegion used_mr = old_gen->used_region();
//minus返回prev_used_region相对于used_mr的差集
MemRegion to_be_cleared_mr = old_gen->prev_used_region().minus(used_mr);
if (!to_be_cleared_mr.is_empty()) {
clear(to_be_cleared_mr);
}
invalidate(used_mr);
}
void invalidate(MemRegion mr, bool whole_heap = false) {
_ct_bs->invalidate(mr, whole_heap);
}
这两方法的调用链是一致的,如下:
7、ClearNoncleanCardWrapper
ClearNoncleanCardWrapper的定义也在cardTableRS.hpp中,其实现如下:
ClearNoncleanCardWrapper::ClearNoncleanCardWrapper(
DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct) :
_dirty_card_closure(dirty_card_closure), _ct(ct) {
//判断是否并行
_is_par = (SharedHeap::heap()->n_par_threads() > 0);
assert(!_is_par ||
(SharedHeap::heap()->n_par_threads() ==
SharedHeap::heap()->workers()->active_workers()), "Mismatch");
}
void ClearNoncleanCardWrapper::do_MemRegion(MemRegion mr) {
assert(mr.word_size() > 0, "Error");
assert(_ct->is_aligned(mr.start()), "mr.start() should be card aligned");
//last方法返回最后一个字节,获取其对应的卡表项
jbyte* cur_entry = _ct->byte_for(mr.last());
//start方法返回起始地址,获取其对应的卡表项
const jbyte* limit = _ct->byte_for(mr.start());
//end_of_non_clean和start_of_non_clean表示脏的卡表项对应的内存区域的起始地址
HeapWord* end_of_non_clean = mr.end();
HeapWord* start_of_non_clean = end_of_non_clean;
//从最后一个卡表项往前遍历
while (cur_entry >= limit) {
//获取cur_entry对应的内存地址
HeapWord* cur_hw = _ct->addr_for(cur_entry);
//clear_card用于将该该卡表项的值置为clean_card
if ((*cur_entry != CardTableRS::clean_card_val()) && clear_card(cur_entry)) {
//如果是脏的卡表项且重置成clean成功,更新start_of_non_clean
start_of_non_clean = cur_hw;
} else {
//碰到一个clean的卡表项
if (start_of_non_clean < end_of_non_clean) {
//如果小于说明之前遍历过脏的卡表项,遍历两者之间的脏的卡表项对应的内存区域
const MemRegion mrd(start_of_non_clean, end_of_non_clean);
_dirty_card_closure->do_MemRegion(mrd);
}
// fast forward through potential continuous whole-word range of clean cards beginning at a word-boundary
if (is_word_aligned(cur_entry)) {
//如果cur_entry地址能够被8整除,即按照8字节对齐
//8个卡表项的大小是4096字节,刚好一个内存页
jbyte* cur_row = cur_entry - BytesPerWord;
//往前遍历,找到下一个脏的卡表项
while (cur_row >= limit && *((intptr_t*)cur_row) == CardTableRS::clean_card_row()) {
cur_row -= BytesPerWord;
}
cur_entry = cur_row + BytesPerWord;
cur_hw = _ct->addr_for(cur_entry);
}
//重置
end_of_non_clean = cur_hw;
start_of_non_clean = cur_hw;
}
//遍历前一个卡表项
cur_entry--;
}
//遍历脏的卡表项对应的内存区域
if (start_of_non_clean < end_of_non_clean) {
const MemRegion mrd(start_of_non_clean, end_of_non_clean);
_dirty_card_closure->do_MemRegion(mrd);
}
}
static jbyte clean_card_val() {
return CardTableModRefBS::clean_card;
}
inline bool ClearNoncleanCardWrapper::clear_card(jbyte* entry) {
if (_is_par) {
return clear_card_parallel(entry);
} else {
//暂且只考虑单线程下的处理
return clear_card_serial(entry);
}
}
inline bool ClearNoncleanCardWrapper::clear_card_serial(jbyte* entry) {
jbyte entry_val = *entry;
assert(entry_val != CardTableRS::clean_card_val(),
"We shouldn't be looking at clean cards, and this should "
"be the only place they get cleaned.");
assert(entry_val != CardTableRS::cur_youngergen_and_prev_nonclean_card,
"This should be possible in the sequential case.");
//将对应的卡表项修改为clean_card
*entry = CardTableRS::clean_card_val();
return true;
}
//判断entry地址能否被8整除
bool ClearNoncleanCardWrapper::is_word_aligned(jbyte* entry) {
return (((intptr_t)entry) & (BytesPerWord-1)) == 0;
}
从上述代码分析可知, ClearNoncleanCardWrapper负责遍历指定内存区域对应的卡表项,找到脏的卡表项将其重置为clean,然后将脏的卡表项对应的内存区域交给_dirty_card_closure的do_MemRegion处理。
二、典型应用场景
1、CollectedHeap ensure_parsability和new_store_pre_barrier
new_store_pre_barrier是给经过JIT编译器编译后的本地代码使用的,这些本地代码通过TLAB慢速分配对象时会忽略barrier,编译器保证本地代码运行时会在这些慢速分配的对象初始化前调用此方法,保证新分配的对象也经过barrier处理了,该方法的调用链如下:
OptoRuntime就是编译器用于生成本地代码的运行时支持,new_store_pre_barrier方法的实现如下:
can_elide_tlab_store_barriers方法表示对于新分配的对象是否需要store barrier处理, CollectedHeap没有提供默认实现,与之类似的还有一个can_elide_initializing_store_barrier方法,不同的是该方法返回true表示对新分配的某个对象不需要store barrier处理。
ensure_parsability方法用于确保Java堆处于parsable state,此方法要求是在STW下调用,子类在改写此方法的实现时必须保证调用了父类的实现。参数retire_tlabs表示是否需要将已经存在的TLAB做填充或者退休处理,从而避免继续TLAB分配对象,必要时分配一个新的TLAB。该方法的调用链如下:
这两方法实现的源码如下:
void CollectedHeap::ensure_parsability(bool retire_tlabs) {
// 校验调用此方法时要么在安全点上,要么在JVM启动初始化过程中
assert(SafepointSynchronize::is_at_safepoint() ||
!is_init_completed(),
"Should only be called at a safepoint or at start-up"
" otherwise concurrent mutator activity may make heap "
" unparsable again");
const bool use_tlab = UseTLAB;
const bool deferred = _defer_initial_card_mark;
// The main thread starts allocating via a TLAB even before it
// has added itself to the threads list at vm boot-up.
assert(!use_tlab || Threads::first() != NULL,
"Attempt to fill tlabs before main thread has been added"
" to threads list is doomed to failure!");
for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
//将所有线程的TLAB执行make_parsable,无论retire_tlabs啥值,TLAB都会被int数组填充剩余空间
if (use_tlab) thread->tlab().make_parsable(retire_tlabs);
//在垃圾回收器开始处理卡表前必须刷新所有的deferred store barriers到卡表中
if (deferred) flush_deferred_store_barrier(thread);
}
}
oop CollectedHeap::new_store_pre_barrier(JavaThread* thread, oop new_obj) {
//将之前的deferred_card_mark刷新到卡表中
flush_deferred_store_barrier(thread);
//can_elide_initializing_store_barrier方法返回true表示这个新创建的对象不需要经过store barrier处理
if (can_elide_initializing_store_barrier(new_obj)) {
assert(thread->deferred_card_mark().is_empty(), "Error");
} else {
//根据对象地址和大小构造一个新的MemRegion
MemRegion mr((HeapWord*)new_obj, new_obj->size());
assert(!mr.is_empty(), "Error");
if (_defer_initial_card_mark) {
//_defer_initial_card_mark为true则设置新的deferred_card_mark
thread->set_deferred_card_mark(mr);
} else {
//_defer_initial_card_mark为false,直接写入卡表
BarrierSet* bs = barrier_set();
assert(bs->has_write_region_opt(), "No write_region() on BarrierSet");
bs->write_region(mr);
}
}
return new_obj;
}
void CollectedHeap::flush_deferred_store_barrier(JavaThread* thread) {
//deferred_card_mark是由线程临时保存的由编译代码慢速分配的会忽略卡表的对象对应的内存区域,
//设置该属性的目的主要是为了提升慢速路径下分配对象的性能
MemRegion deferred = thread->deferred_card_mark();
if (!deferred.is_empty()) {
//如果非空,_defer_initial_card_mark必须为true,该属性默认为false
assert(_defer_initial_card_mark, "Otherwise should be empty");
{
// Verify that the storage points to a parsable object in heap
DEBUG_ONLY(oop old_obj = oop(deferred.start());)
//校验是在Java堆中分配的对象
assert(is_in(old_obj), "Not in allocated heap");
//can_elide_initializing_store_barrier返回false表示需要store_barrier处理
assert(!can_elide_initializing_store_barrier(old_obj),
"Else should have been filtered in new_store_pre_barrier()");
//校验该对象是oop,且deferred大小跟oop大小一致
assert(old_obj->is_oop(true), "Not an oop");
assert(deferred.word_size() == (size_t)(old_obj->size()),
"Mismatch: multiple objects?");
}
BarrierSet* bs = barrier_set();
//交易bs支持write_region方法
assert(bs->has_write_region_opt(), "No write_region() on BarrierSet");
//将MemRegion对应的卡表项标记为脏的
bs->write_region(deferred);
//清除原有的defered,初始化一个空的MemRegion
thread->set_deferred_card_mark(MemRegion());
}
//校验defered为空
assert(thread->deferred_card_mark().is_empty(), "invariant");
}
2、System.arraycopy
System.arraycopy方法时集合类框架在底层数组扩容或者缩容时经常使用的方法,用来在两个数组间复制元素,该方法是一个本地方法,具体实现在jdk/src/share/native/java/lang/System.c中,如下:
JVM_ArrayCopy的实现如下:
s是对象数组,对应的Klass子类是ObjArrayKlass,该类的copy_array方法的实现如下:
void ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d,
int dst_pos, int length, TRAPS) {
assert(s->is_objArray(), "must be obj array");
//d必须是对象数组
if (!d->is_objArray()) {
THROW(vmSymbols::java_lang_ArrayStoreException());
}
//校验参数合法
if (src_pos < 0 || dst_pos < 0 || length < 0) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
//校验是否超出数组长度
if ( (((unsigned int) length + (unsigned int) src_pos) > (unsigned int) s->length())
|| (((unsigned int) length + (unsigned int) dst_pos) > (unsigned int) d->length()) ) {
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
}
// length为0,即不执行复制动作
if (length==0) {
return;
}
//根据是否压缩指针
if (UseCompressedOops) {
//获取两个数组对应位置的元素
narrowOop* const src = objArrayOop(s)->obj_at_addr<narrowOop>(src_pos);
narrowOop* const dst = objArrayOop(d)->obj_at_addr<narrowOop>(dst_pos);
do_copy<narrowOop>(s, src, d, dst, length, CHECK);
} else {
oop* const src = objArrayOop(s)->obj_at_addr<oop>(src_pos);
oop* const dst = objArrayOop(d)->obj_at_addr<oop>(dst_pos);
do_copy<oop> (s, src, d, dst, length, CHECK);
}
}
template <class T> void ObjArrayKlass::do_copy(arrayOop s, T* src,
arrayOop d, T* dst, int length, TRAPS) {
BarrierSet* bs = Universe::heap()->barrier_set();
//要求BarrierSet支持这两个动作,以保证性能,has_write_ref_array_pre_opt在BarrierSet默认为true,
//has_write_ref_array_opt在BarrierSet的子类实现中默认为true
assert(bs->has_write_ref_array_opt(), "Barrier set must have ref array opt");
assert(bs->has_write_ref_array_pre_opt(), "For pre-barrier as well.");
if (s == d) {
//如果是同一个数组内部复制则不需要转换检查
assert(length > 0, "sanity check");
bs->write_ref_array_pre(dst, length);
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// We have to make sure all elements conform to the destination array
//获取两个数组的元素类型
Klass* bound = ObjArrayKlass::cast(d->klass())->element_klass();
Klass* stype = ObjArrayKlass::cast(s->klass())->element_klass();
if (stype == bound || stype->is_subtype_of(bound)) {
//如果原数组元素跟目标数组元素类型相同或者是其子类,同样不需要检查了
bs->write_ref_array_pre(dst, length);
Copy::conjoint_oops_atomic(src, dst, length);
} else {
// slow case: need individual subtype checks
// note: don't use obj_at_put below because it includes a redundant store check
T* from = src;
T* end = from + length;
//遍历原数组中需要拷贝的元素
for (T* p = dst; from < end; from++, p++) {
//读取数组元素oop
T element = *from;
bool element_is_null = oopDesc::is_null(element);
oop new_val = element_is_null ? oop(NULL)
: oopDesc::decode_heap_oop_not_null(element);
//如果是目标数组元素类型的子类
if (element_is_null ||
(new_val->klass())->is_subtype_of(bound)) {
bs->write_ref_field_pre(p, new_val);
//执行拷贝
*p = element;
} else {
//计算已拷贝的元素个数
const size_t pd = pointer_delta(p, dst, (size_t)heapOopSize);
assert(pd == (size_t)(int)pd, "length field overflow");
//已拷贝的元素对应的卡表项置为脏的
bs->write_ref_array((HeapWord*)dst, pd);
//抛出异常
THROW(vmSymbols::java_lang_ArrayStoreException());
return;
}
}
}
}
//将目标数组内存对应卡表项置为脏的
bs->write_ref_array((HeapWord*)dst, length);
}
3、TemplateTable::_bs
_bs是TemplateTable的静态属性,static BarrierSet*,该属性在initialize方法中完成初始化,如下图:
该属性的调用链如下:
其中aatore方法就是aastore指令的实现,该指令用于从操作数栈中读取一个引用类型数据写入数组中,该方法中写入数组元素的实现如下:
fast_storefield方法用于实现OpenJDK自定义的以fast开头的字节码指令,如下图:
用于写入静态属性的putstatic指令和用于写入实例属性的putfield指令会根据属性的类型路由到不同的fast指令处理,其实现如下:
putfield_or_static方法中针对引用类型的属性的处理如下:
fast_storefield方法中用于处理引用类型的属性的实现如下:
即TemplateTable中涉及引用类型的字段或者数组元素的写入最终都会调用do_oop_store方法。
4、TemplateTable do_oop_store
do_oop_store方法就是解释器解释执行时实际调用BarrierSet的入口,该方法的实现如下:
static void do_oop_store(InterpreterMacroAssembler* _masm,
Address obj,
Register val,
BarrierSet::Name barrier,
bool precise) {
assert(val == noreg || val == rax, "parameter is just for looks");
switch (barrier) {
#if INCLUDE_ALL_GCS
case BarrierSet::G1SATBCT:
case BarrierSet::G1SATBCTLogging:
{
//将写入地址obj拷贝到rdx中
if (obj.index() == noreg && obj.disp() == 0) {
if (obj.base() != rdx) {
__ movq(rdx, obj.base());
}
} else {
__ leaq(rdx, obj);
}
//底层调用SharedRuntime::g1_wb_pre方法
__ g1_write_barrier_pre(rdx /* obj */,
rbx /* pre_val */,
r15_thread /* thread */,
r8 /* tmp */,
val != noreg /* tosca_live */,
false /* expand_call */);
//如果值是NULL
if (val == noreg) {
__ store_heap_oop_null(Address(rdx, 0));
} else {
如果值不是NULL
Register new_val = val;
if (UseCompressedOops) {
new_val = rbx;
//将val的值拷贝到new_val中,避免store_heap_oop执行时将val encode了
__ movptr(new_val, val);
}
//将值val写入到目标地址里面,处理时如果开启UseCompressedOops会自动将指针encode
__ store_heap_oop(Address(rdx, 0), val);
//底层最终调用SharedRuntime::g1_wb_post
__ g1_write_barrier_post(rdx /* store_adr */,
new_val /* new_val */,
r15_thread /* thread */,
r8 /* tmp */,
rbx /* tmp2 */);
}
}
break;
#endif // INCLUDE_ALL_GCS
case BarrierSet::CardTableModRef:
case BarrierSet::CardTableExtension:
{
//如果值是Null
if (val == noreg) {
__ store_heap_oop_null(obj);
} else {
//如果值不是Null
__ store_heap_oop(obj, val);
// flatten object address if needed
if (!precise || (obj.index() == noreg && obj.disp() == 0)) {
__ store_check(obj.base());
} else {
//将写入地址拷贝到rdx中
__ leaq(rdx, obj);
//store_check方法会获取byte_map_base地址,计算obj对应的卡表项,将其置为0,即dirty_card
__ store_check(rdx);
}
}
}
break;
case BarrierSet::ModRef:
case BarrierSet::Other:
if (val == noreg) {
__ store_heap_oop_null(obj);
} else {
__ store_heap_oop(obj, val);
}
break;
default :
ShouldNotReachHere();
}
}
其中g1_wb_pre和g1_wb_post的实现如下:
5、总结
上述应用场景基本说明了BarrierSet类的用途,就相当于一个拦截器,在写入属性或者数组元素前后执行的处理动作,目前所有场景都是卡表中的卡表项被标记成dirty_card,至于什么时候清除变成clean_card,怎样根据卡表找到跨代引用等细节实现在GC的相关实现子类中,BarrierSet及其子类不涉及。
另外需要澄清的一点是,卡表并不是单单针对老年代,而是整个Java堆,卡表并没有直接记录老年代对年轻代的引用关系,只是单纯的记录了相对于上一次GC结束后发生引用关系变动的内存区域而已,至于怎样遍历这些脏的内存区域并形成具体的引用关系图谱的细节也是在GC的相关实现子类中,BarrierSet及其子类不涉及。