目录
二、G1SATBCardTableLoggingModRefBS
1、write_ref_field_pre_work /write_ref_array_pre
2、set_card_claimed /g1_mark_as_young /mark_card_deferred
三、G1SATBCardTableLoggingModRefBS
2、G1SATBCardTableLoggingModRefBSChangedListener
3、write_ref_field_work /write_region_work /write_ref_array_work /invalidate
本篇继续上一篇《Hotspot 垃圾回收之BarrierSet(一) 源码解析》探讨BarrierSet其他的子类的实现。
一、CardTableExtension
1、定义
CardTableExtension的定义在hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.hpp中,该类继承自CardTableModRefBS,是为ParallelScavengeHeap定制的卡表实现,参考其构造方法的调用链,如下:
CardTableExtension添加了枚举ExtendedCardValue,表示扩展的CardValue,如下:
CardTableExtension添加了一个新的用于并行遍历卡表的scavenge_contents_parallel方法,该方法的调用链如下:
因为涉及ParallelScavengeHeap的实现细节,这里暂时不深入研究了,重点关注重写的一个方法resize_covered_region的实现。
2、resize_covered_region
resize_covered_region方法用于调整MemRegion对应的_covered和_committed元素,当前实现下_committed元素会随着MemRegion扩容而扩容,因为安全问题不会随着MemRegion缩容而缩容,_covered元素无论扩容和缩容都跟原MemRegion保持一致。该方法假定MemRegion要么是新增的,要么就是起始地址不变,终止地址改变,要么就是终止地址不变,起始地址改变这三种情形,父类的实现只支持新增和起始地址不变两种,子类新增了终止地址不变的支持。源码说明如下:
//resize_covered_region方法假定MemRegion要么是新增的,要么就是起始地址不变,终止地址改变,要么就是终止地址不变,起始地址改变
//这三种情形,父类的实现只支持新增和起始地址不变两种,子类新增了终止地址不变的支持
void CardTableExtension::resize_covered_region(MemRegion new_region) {
for (int i = 0; i < _cur_covered_regions; i++) {
if (_covered[i].start() == new_region.start()) {
//找到一个基地址相同的_covered元素,说明new_region是终止地址发生改变了,调用父类实现即可
resize_covered_region_by_start(new_region);
return;
}
if (_covered[i].start() > new_region.start()) {
break;
}
}
//没有找到一个基地址相同的_covered元素
int changed_region = -1;
for (int j = 0; j < _cur_covered_regions; j++) {
if (_covered[j].end() == new_region.end()) {
//找到一个终止地址相同的_covered元素,即基地址在变化
changed_region = j;
assert(changed_region != -1, "Don't expect to add a covered region");
//校验两者的大小不一致
assert(_covered[changed_region].byte_size() != new_region.byte_size(),
"The sizes should be different here");
resize_covered_region_by_end(changed_region, new_region);
return;
}
}
// 没有一个基地址或者终止地址相同的_covered元素,说明new_region是一个全新的,
//需要添加一个新的_covered元素,调用父类实现即可
assert(_cur_covered_regions < _max_covered_regions,
"An existing region should have been found");
resize_covered_region_by_start(new_region);
}
//基地址不变,终止地址变了,通过父类的resize_covered_region修改
void CardTableExtension::resize_covered_region_by_start(MemRegion new_region) {
CardTableModRefBS::resize_covered_region(new_region);
debug_only(verify_guard();)
}
void CardTableExtension::resize_covered_region_by_end(int changed_region,
MemRegion new_region) {
//当前在安全点上,只有在GC的时候才能扩展
assert(SafepointSynchronize::is_at_safepoint(),
"Only expect an expansion at the low end at a GC");
debug_only(verify_guard();)
//执行扩容或者缩容,返回true表示扩容
if (resize_commit_uncommit(changed_region, new_region)) {
//如果执行了扩容则更新对应commited元素
resize_update_committed_table(changed_region, new_region);
}
//如果扩容了,将扩容内存对应的卡表项置为clean_card
resize_update_card_table_entries(changed_region, new_region);
//更新covered元素,重新排序
resize_update_covered_table(changed_region, new_region);
//打印日志
if (TraceCardTableModRefBS) {
int ind = changed_region;
gclog_or_tty->print_cr("CardTableModRefBS::resize_covered_region: ");
gclog_or_tty->print_cr(" "
" _covered[%d].start(): " INTPTR_FORMAT
" _covered[%d].last(): " INTPTR_FORMAT,
ind, p2i(_covered[ind].start()),
ind, p2i(_covered[ind].last()));
gclog_or_tty->print_cr(" "
" _committed[%d].start(): " INTPTR_FORMAT
" _committed[%d].last(): " INTPTR_FORMAT,
ind, p2i(_committed[ind].start()),
ind, p2i(_committed[ind].last()));
gclog_or_tty->print_cr(" "
" byte_for(start): " INTPTR_FORMAT
" byte_for(last): " INTPTR_FORMAT,
p2i(byte_for(_covered[ind].start())),
p2i(byte_for(_covered[ind].last())));
gclog_or_tty->print_cr(" "
" addr_for(start): " INTPTR_FORMAT
" addr_for(last): " INTPTR_FORMAT,
p2i(addr_for((jbyte*) _committed[ind].start())),
p2i(addr_for((jbyte*) _committed[ind].last())));
}
debug_only(verify_guard();)
}
//判断是否缩容或者扩容,如果扩容则返回true,扩容只完成了内存commit而已,并未更新对应的covered和commited元素
//当前实现缩容不安全,没有实际缩容
bool CardTableExtension::resize_commit_uncommit(int changed_region,
MemRegion new_region) {
bool result = false;
MemRegion cur_committed = _committed[changed_region];
assert(_covered[changed_region].end() == new_region.end(),
"The ends of the regions are expected to match");
//如果changed_region之前的commit元素与该元素的内存区域有重叠则返回之前的commit元素的起始地址
HeapWord* min_prev_start = lowest_prev_committed_start(changed_region);
if (min_prev_start < cur_committed.start()) {
//说明存在两个_committed元素的内存区域有重叠的情形,需要将这些重叠的合并
MemRegion new_committed =
MemRegion(min_prev_start, cur_committed.end());
cur_committed = new_committed;
}
//获取new_region起始地址对应的卡表项地址,并做内存对齐
jbyte* new_start = byte_for(new_region.start());
HeapWord* new_start_aligned =
(HeapWord*)align_size_down((uintptr_t)new_start, os::vm_page_size());
if (new_start_aligned < cur_committed.start()) {
//获取终止地址,不能超过_guard_region
HeapWord* new_end_for_commit =
MIN2(cur_committed.end(), _guard_region.start());
if(new_start_aligned < new_end_for_commit) {
//执行commit扩展内存
MemRegion new_committed =
MemRegion(new_start_aligned, new_end_for_commit);
os::commit_memory_or_exit((char*)new_committed.start(),
new_committed.byte_size(), !ExecMem,
"card table expansion");
}
result = true;
} else if (new_start_aligned > cur_committed.start()) {
// 需要执行uncommit缩容,因为上面执行了cur_committed的合并,所以缩容的区域有可能属于另外一个MemRegion,不安全
assert(!result, "Should be false with current workaround");
}
assert(_committed[changed_region].end() == cur_committed.end(),
"end should not change");
return result;
}
HeapWord* CardTableExtension::lowest_prev_committed_start(int ind) const {
assert(_cur_covered_regions >= 0, "Expecting at least on region");
HeapWord* min_start = _committed[ind].start();
for (int j = 0; j < ind; j++) {
HeapWord* this_start = _committed[j].start();
//如果两个有交集
if ((this_start < min_start) &&
!(_committed[j].intersection(_committed[ind])).is_empty()) {
min_start = this_start;
}
}
return min_start;
}
//根据new_region的起始地址修改changed_region对应的_committed元素
void CardTableExtension::resize_update_committed_table(int changed_region,
MemRegion new_region) {
jbyte* new_start = byte_for(new_region.start());
// Set the new start of the committed region
HeapWord* new_start_aligned =
(HeapWord*)align_size_down((uintptr_t)new_start,
os::vm_page_size());
MemRegion new_committed = MemRegion(new_start_aligned,
_committed[changed_region].end());
_committed[changed_region] = new_committed;
_committed[changed_region].set_start(new_start_aligned);
}
void CardTableExtension::resize_update_card_table_entries(int changed_region,
MemRegion new_region) {
debug_only(verify_guard();)
MemRegion original_covered = _covered[changed_region];
//new_region的基地址不能超过堆内存的基地址
jbyte* entry;
if (new_region.start() < _whole_heap.start()) {
entry = byte_for(_whole_heap.start());
} else {
entry = byte_for(new_region.start());
}
jbyte* end = byte_for(original_covered.start());
//如果扩容了则entry会小于end,将扩容内存对应卡表项置为clean_card
while (entry < end) { *entry++ = clean_card; }
}
void CardTableExtension::resize_update_covered_table(int changed_region,
MemRegion new_region) {
//更新_covered元素
_covered[changed_region].set_start(new_region.start());
_covered[changed_region].set_word_size(new_region.word_size());
// 将所有_covered元素按照起始地址升序的方式排序
for (int i = _cur_covered_regions-1 ; i > 0; i--) {
if (_covered[i].start() < _covered[i-1].start()) {
MemRegion covered_mr = _covered[i-1];
_covered[i-1] = _covered[i];
_covered[i] = covered_mr;
MemRegion committed_mr = _committed[i-1];
_committed[i-1] = _committed[i];
_committed[i] = committed_mr;
break;
}
}
}
二、G1SATBCardTableLoggingModRefBS
G1SATBCardTableModRefBS继承自CardTableModRefBSForCTRS,CardTableModRefBSForCTRS的定义和CardTableModRefBS在同一个cardTableModRefBS.hpp中,该类的定义很简单,如下:
G1SATBCardTableModRefBS的定义在hotspot/src/share/vm/gc_implementation/g1/g1SATBCardTableModRefBS.hpp中,是为了支持G1的SATB(snapshot-at-the-beginning)并发标记算法而定制的特殊BarrierSet,改写了父类CardTableModRefBS的诸多方法的实现,比如:
该方法在CardTableModRefBS中返回false,表示不支持在写入引用类型的属性时执行预处理,重点关注以下方法的改写。
1、write_ref_field_pre_work /write_ref_array_pre
这两方法在CardTableModRefBS中因为has_write_ref_pre_barrier方法返回false,所以都是默认采用父类BarrierSet的空实现,如下图:
G1SATBCardTableModRefBS中的实现如下:
virtual void write_ref_field_pre_work(oop* field, oop new_val) {
inline_write_ref_field_pre(field, new_val);
}
virtual void write_ref_field_pre_work(narrowOop* field, oop new_val) {
inline_write_ref_field_pre(field, new_val);
}
template <class T> inline void inline_write_ref_field_pre(T* field, oop newVal) {
write_ref_field_pre_static(field, newVal);
}
template <class T> static void write_ref_field_pre_static(T* field, oop newVal) {
T heap_oop = oopDesc::load_heap_oop(field);
//如果oop非空
if (!oopDesc::is_null(heap_oop)) {
//如果采用指针压缩,即T是narrowOop时需要做decode还原
enqueue(oopDesc::decode_heap_oop(heap_oop));
}
}
void G1SATBCardTableModRefBS::write_ref_array_pre(oop* dst, int count, bool dest_uninitialized) {
if (!dest_uninitialized) {
write_ref_array_pre_work(dst, count);
}
}
void G1SATBCardTableModRefBS::write_ref_array_pre(narrowOop* dst, int count, bool dest_uninitialized) {
if (!dest_uninitialized) {
write_ref_array_pre_work(dst, count);
}
}
template <class T> void
G1SATBCardTableModRefBS::write_ref_array_pre_work(T* dst, int count) {
if (!JavaThread::satb_mark_queue_set().is_active()) return;
T* elem_ptr = dst;
//遍历dst之后count个oop
for (int i = 0; i < count; i++, elem_ptr++) {
T heap_oop = oopDesc::load_heap_oop(elem_ptr);
if (!oopDesc::is_null(heap_oop)) {
enqueue(oopDesc::decode_heap_oop_not_null(heap_oop));
}
}
}
void G1SATBCardTableModRefBS::enqueue(oop pre_val) {
//必须是oop
assert(pre_val->is_oop(true), "Error");
//如果satb quene不是活跃的则返回
if (!JavaThread::satb_mark_queue_set().is_active()) return;
Thread* thr = Thread::current();
//如果是Java线程
if (thr->is_Java_thread()) {
JavaThread* jt = (JavaThread*)thr;
//加入到队列中
jt->satb_mark_queue().enqueue(pre_val);
} else {
//如果是JVM线程
MutexLockerEx x(Shared_SATB_Q_lock, Mutex::_no_safepoint_check_flag);
JavaThread::satb_mark_queue_set().shared_satb_queue()->enqueue(pre_val);
}
}
上述方法实现的核心最终都落脚到enqueue方法,落脚到ObjPtrQueue和SATBMarkQueueSet两个类的实现,下一篇博客会详细探讨这两个类的实现。
2、set_card_claimed /g1_mark_as_young /mark_card_deferred
CardTableModRefBS中只用到了dirty_card和clean_card两种CardValue,G1SATBCardTableModRefBS增加了claimed_card和deferred_card两种,并自定义了一个扩展的g1_young_gen,表示该卡表项对应的内存区域是一个young gen,其定义如下:
其中claimed_card可以和其他的非clean_card共存,除dirty_card外;deferred_card是和claimed_card配合使用,只能从clean_card或者claimed_card转换成deferred_card。上面三个方法就是将对应的卡表项设置为对应的CardValue,其实现如下:
void set_card_claimed(size_t card_index) {
//获取对应卡表项的值
jbyte val = _byte_map[card_index];
if (val == clean_card_val()) {
val = (jbyte)claimed_card_val();
} else {
//使用或运算,会保留卡表项原来的状态
val |= (jbyte)claimed_card_val();
}
_byte_map[card_index] = val;
}
void G1SATBCardTableModRefBS::g1_mark_as_young(const MemRegion& mr) {
//计算mr对应的起始卡表项
jbyte *const first = byte_for(mr.start());
jbyte *const last = byte_after(mr.last());
// UseMemSetInBOT默认为true,表示是否在GC的代码中使用memset完成内存值设置
if (UseMemSetInBOT) {
memset(first, g1_young_gen, last - first);
} else {
//通过指针的方式遍历
for (jbyte* i = first; i < last; i++) {
*i = g1_young_gen;
}
}
}
bool G1SATBCardTableModRefBS::mark_card_deferred(size_t card_index) {
//获取对应卡表项的值
jbyte val = _byte_map[card_index];
//如果已经是deferred_card
if ((val & (clean_card_mask_val() | deferred_card_val())) == deferred_card_val()) {
return false;
}
if (val == g1_young_gen) {
//不需要跟踪指向年轻代的指针
return false;
}
// Cached bit can be installed either on a clean card or on a claimed card.
jbyte new_val = val;
if (val == clean_card_val()) {
new_val = (jbyte)deferred_card_val();
} else {
//该卡表项必须原来是claimed_card
if (val & claimed_card_val()) {
new_val = val | (jbyte)deferred_card_val();
}
}
if (new_val != val) {
//原子的更新状态,更新失败不影响
Atomic::cmpxchg(new_val, &_byte_map[card_index], val);
}
return true;
}
static int claimed_card_val() { return claimed_card; }
static int clean_card_val() { return clean_card; }
static int deferred_card_val() { return deferred_card; }
三、G1SATBCardTableLoggingModRefBS
G1SATBCardTableLoggingModRefBS跟G1SATBCardTableLoggingModRefBS定义在同一个g1SATBCardTableModRefBS.hpp中,G1实际使用的是G1SATBCardTableLoggingModRefBS作为BarrierSet的实现类,该类同样对父类的实现做了大幅调整,以适配G1的垃圾回收机制,重点关注以下方法的改写。
1、构造方法和initialize
G1SATBCardTableLoggingModRefBS新增了两个属性,如下:
这两个都是在构造方法中完成初始化,如下:
G1SATBCardTableLoggingModRefBS将父类initialize的实现改成了空实现,增加了一个有参数的initialize方法完成其初始化,如下:
该方法的实现源码如下:
void G1SATBCardTableLoggingModRefBS::initialize(G1RegionToSpaceMapper* mapper) {
mapper->set_mapping_changed_listener(&_listener);
//G1RegionToSpaceMapper相当于整个卡表了,没有走父类的申请内存逻辑
_byte_map_size = mapper->reserved().byte_size();
_guard_index = cards_required(_whole_heap.word_size()) - 1;
_last_valid_index = _guard_index - 1;
HeapWord* low_bound = _whole_heap.start();
HeapWord* high_bound = _whole_heap.end();
//只有一个covered元素,并且元素就是整个Java堆
_cur_covered_regions = 1;
_covered[0] = _whole_heap;
_byte_map = (jbyte*) mapper->reserved().start();
byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift);
assert(byte_for(low_bound) == &_byte_map[0], "Checking start of map");
assert(byte_for(high_bound-1) <= &_byte_map[_last_valid_index], "Checking end of map");
if (TraceCardTableModRefBS) {
//打印日志
gclog_or_tty->print_cr("G1SATBCardTableModRefBS::G1SATBCardTableModRefBS: ");
gclog_or_tty->print_cr(" "
" &_byte_map[0]: " INTPTR_FORMAT
" &_byte_map[_last_valid_index]: " INTPTR_FORMAT,
p2i(&_byte_map[0]),
p2i(&_byte_map[_last_valid_index]));
gclog_or_tty->print_cr(" "
" byte_map_base: " INTPTR_FORMAT,
p2i(byte_map_base));
}
}
该方法相比父类的实现最大的区别有两点,一是没有通过ReservedSpace申请内存,传入的参数G1RegionToSpaceMapper相当于一个已经申请好内存的卡表;二是没有初始化_committed数组,coverd元素只有一个,就是_whole_heap。
该方法的调用链如下:
构造方法的调用链如下:
2、G1SATBCardTableLoggingModRefBSChangedListener
G1SATBCardTableLoggingModRefBSChangedListener的定义和G1SATBCardTableLoggingModRefBS在同一个文件中,表示G1下CardTable内存变化后触发的动作,该类的定义如下:
G1MappingChangedListener就是一个定义了on_commit方法的接口类,该类的定义如下:
G1SATBCardTableLoggingModRefBSChangedListener的on_commit方法的实现如下:
即将start_idx对应的内存区域对应的卡表项初始化为clean_card。
3、write_ref_field_work /write_region_work /write_ref_array_work /invalidate
上述方法在父类中的实现都是将对应的卡表项置为dirty_card而已,G1SATBCardTableLoggingModRefBS的实现多了一步,将这些dirty_card的卡表项放入DirtyCardQueue中管理起来,避免卡表的扫描。上述方法的实现如下:
void
G1SATBCardTableLoggingModRefBS::write_ref_field_work(void* field,
oop new_val,
bool release) {
//获取对应的卡表项
volatile jbyte* byte = byte_for(field);
//不处理g1_young_gen的卡表项
if (*byte == g1_young_gen) {
return;
}
OrderAccess::storeload();
if (*byte != dirty_card) {
//将卡表项置为dirty_card
*byte = dirty_card;
Thread* thr = Thread::current();
//将该卡表项放入DirtyCardQueue中
if (thr->is_Java_thread()) {
JavaThread* jt = (JavaThread*)thr;
jt->dirty_card_queue().enqueue(byte);
} else {
MutexLockerEx x(Shared_DirtyCardQ_lock,
Mutex::_no_safepoint_check_flag);
_dcqs.shared_dirty_card_queue()->enqueue(byte);
}
}
}
void write_region_work(MemRegion mr) { invalidate(mr); }
void write_ref_array_work(MemRegion mr) { invalidate(mr); }
void
G1SATBCardTableLoggingModRefBS::invalidate(MemRegion mr, bool whole_heap) {
//获取mr内存区域对应的起止卡表项
volatile jbyte* byte = byte_for(mr.start());
jbyte* last_byte = byte_for(mr.last());
Thread* thr = Thread::current();
if (whole_heap) {
//如果是整个堆内存
while (byte <= last_byte) {
*byte = dirty_card;
byte++;
}
} else {
//跳过连续的表示young gen的卡表项
for (; byte <= last_byte && *byte == g1_young_gen; byte++);
if (byte <= last_byte) {
OrderAccess::storeload();
// Enqueue if necessary.
if (thr->is_Java_thread()) {
JavaThread* jt = (JavaThread*)thr;
for (; byte <= last_byte; byte++) {
//过滤g1_young_gen
if (*byte == g1_young_gen) {
continue;
}
if (*byte != dirty_card) {
//置为dirty_card,并放入队列中
*byte = dirty_card;
jt->dirty_card_queue().enqueue(byte);
}
}
} else {
//非Java线程需要获取锁Shared_DirtyCardQ_lock
MutexLockerEx x(Shared_DirtyCardQ_lock,
Mutex::_no_safepoint_check_flag);
for (; byte <= last_byte; byte++) {
////过滤g1_young_gen
if (*byte == g1_young_gen) {
continue;
}
if (*byte != dirty_card) {
*byte = dirty_card;
_dcqs.shared_dirty_card_queue()->enqueue(byte);
}
}
}
}
}
}
4、resize_covered_region
G1SATBCardTableLoggingModRefBS下resize_covered_region不会被调用,因为covered元素只有一个,就是表示整个堆内存的whole_heap,所以将该方法改成一个空实现,如下图:
来源:CSDN
作者:孙大圣666
链接:https://blog.csdn.net/qq_31865983/article/details/103720830