Repository navigation
g1 evacuation Mixed_GC_Main
ceragon edited this page Dec 30, 2021
·
4 revisions
GC 是 Garbage Collection 的简称,在 G1 中 GC 采用的是标记复制算法.
G1 GC 过程是把存活对象从一个 region 复制到另外一个 region,然后重置原 region,这个过程就像是在转移对象。
所以下文涉及到 'GC 过程' 的中文表述时,我会写成年轻代转移和老年代转移,而不是回收,特此声明。
bool G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
// .........
}- 预测年轻代转移耗时
- 选择老年代的转移集合
- 处理大对象 region
- 根扫描
- 处理转移专用的卡表
- 处理记忆集合
- 执行年轻代的转移
- 执行老年代的转移
- 统计未处理的 card table 预计耗时
- 统计执行年轻代 region 转移到预计耗时
相关代码
- 以下是预测年轻代转移耗时的相关位置
void G1Policy::finalize_collection_set(
double target_pause_time_ms, // 在此方法中 = MaxGCPauseMillis
G1SurvivorRegions* survivor // survivor region 的列表
) {
// time_remaining_ms = target_pause_time_ms - 转移年轻代的预测耗时。所以该值是剩余可用的暂停时间,
double time_remaining_ms = _collection_set->finalize_young_part(target_pause_time_ms, survivor);
}- 以下是主体逻辑
double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors) {
// TODO:
finalize_incremental_building();
// 待处理的卡表数量
size_t pending_cards = _policy->pending_cards();
// 根据卡表的数量预测处理耗时
double base_time_ms = _policy->predict_base_elapsed_time_ms(pending_cards);
// 还剩多少可用时间
double time_remaining_ms = MAX2(target_pause_time_ms - base_time_ms, 0.0);
// survivor 区的 region 个数
uint survivor_region_length = survivors->length();
// eden 区的 region 个数
uint eden_region_length = _g1h->eden_regions_count();
// 给 G1CollectionSet 类的成员变量赋值
init_region_lengths(eden_region_length, survivor_region_length);
// 将全部新生代 region 的索引放入到 _heap_region_indices 数组中
verify_young_cset_indices();
// 将全部 survivor region 标记为 eden region,并初始化 (只设置size字段,不清除数据)
survivors->convert_to_eden();
// 这里的 _inc_bytes_used_before 是年轻代的总空间占用
// 记录一下 GC 前的年轻代空间占用
_bytes_used_before = _inc_bytes_used_before;
// _inc_predicted_elapsed_time_ms 是预测出来的转移年轻代 region 花费的时间
time_remaining_ms = MAX2(time_remaining_ms - _inc_predicted_elapsed_time_ms, 0.0);
// 记录一下 GC 前的年轻代 region 个数
set_recorded_rs_lengths(_inc_recorded_rs_lengths);
// 如果 young region 的转移执行完成之后,还剩余多少可用的时间
return time_remaining_ms;
}解析
- _collection_set 被下面的方法赋值
void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) {
_collection_set = collection_set;
// 初始化 _collection_set
_collection_set->start_incremental_building();
}- 在下面的方法中会调用上面的
init()方法,且下面的方法会在 Jvm 启动阶段调用
class G1CollectedHeap : public CollectedHeap {
G1CollectionSet _collection_set;
};
jint G1CollectedHeap::initialize() {
g1_policy()->init(this, &_collection_set);
}相关代码
- 执行选择老年代转移集合的相关代码
void G1Policy::finalize_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) {
// 就是上面介绍过的预测年轻代转移到耗时
double time_remaining_ms = _collection_set->finalize_young_part(target_pause_time_ms, survivor);
// 选择老年代转移集合的主体方法
_collection_set->finalize_old_part(time_remaining_ms);
}- 主体代码
void G1CollectionSet::finalize_old_part(double time_remaining_ms) {
// 必须处于 mixed gc 中
if (collector_state()->in_mixed_phase()) {
// 当前 cset 元素个数的 8% 为下限
const uint min_old_cset_length = _policy->calc_min_old_cset_length();
// 当前 heap 内已激活 region 总数的 10% 为上限
const uint max_old_cset_length = _policy->calc_max_old_cset_length();
// 从 cset 中获取一个 region(实际上是从排好序的 list 中获取第一个 region)
HeapRegion* hr = cset_chooser()->peek();
while (hr != NULL) {
// 每一次 mix gc 时,纳入到转移范围的老年代 region 有个数量上限
if (old_region_length() >= max_old_cset_length) {
break;
}
// 剩余待回收的空间大小
size_t reclaimable_bytes = cset_chooser()->remaining_reclaimable_bytes();
// 可回收率
double reclaimable_percent = _policy->reclaimable_bytes_percent(reclaimable_bytes);
// 可回收率的阈值
double threshold = (double) G1HeapWastePercent;
if (reclaimable_percent <= threshold) {
// 低于阈值就不用继续添加 region 了
break;
}
// 根据历史经验预测一下这个 region 转移需要花费的时间
double predicted_time_ms = predict_region_elapsed_time_ms(hr);
// 只要年轻代的空间是自适应的,这里就是 true
if (check_time_remaining) {
// 如果把当前的 region 加到转移集合中,会导致超出暂停时间,则为真
if (predicted_time_ms > time_remaining_ms) {
// 如果当前到转移集合还未满足最小数量,则仍然会把这个超出暂停时间的 region 加到转移集合中
if (old_region_length() >= min_old_cset_length) {
break;
}
}
} else {
// 检查转移集合的个数是否满足最小要求
if (old_region_length() >= min_old_cset_length) {
break;
}
}
// 从 cset 中移除当前 region
cset_chooser()->pop();
// 提前设置一下这个 region 需要在转移后删除
_g1h->old_set_remove(hr);
// 填到转移集合中
add_old_region(hr);
// 取出下一个 region
hr = cset_chooser()->peek();
}
}
}解析
uint G1Policy::calc_min_old_cset_length() const {
// cset 内的 region 个数
const size_t region_num = (size_t) cset_chooser()->length();
// 预期的 Mixed GC 执行次数,G1MixedGCCountTarget 默认值是 8
const size_t gc_num = (size_t) MAX2(G1MixedGCCountTarget, (uintx) 1);
// 每次 GC 需要处理几个 region
size_t result = region_num / gc_num;
if (result * gc_num < region_num) {
// 向上取整
result += 1;
}
return (uint) result;
}解析
uint G1Policy::calc_max_old_cset_length() const {
// g1h 是 g1 的堆空间对象
const G1CollectedHeap *g1h = G1CollectedHeap::heap();
// 堆中已激活的 region 个数
const size_t region_num = g1h->num_regions();
// G1OldCSetRegionThresholdPercent 一个阈值,单位是百分比,默认是 10%
const size_t perc = (size_t) G1OldCSetRegionThresholdPercent;
// 计算堆内总的 region 个数的 10% 是多少。包括老年代和新生代 region
size_t result = region_num * perc / 100;
if (100 * result < region_num * perc) {
// 向上取整
result += 1;
}
return (uint) result;
}相关代码
bool G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
// 代码行数 2929
register_humongous_regions_with_cset();
}void G1CollectedHeap::register_humongous_regions_with_cset() {
if (!G1EagerReclaimHumongousObjects) {
return;
}
RegisterHumongousWithInCSetFastTestClosure cl;
// 尝试把一个大对象 region 放入到一个集合中
heap_region_iterate(&cl);
// 是否有回收候选 region
_has_humongous_reclaim_candidates = cl.candidate_humongous() > 0;
// 刷到全局的脏卡队列中
cl.flush_rem_set_entries();
}class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
virtual bool do_heap_region(HeapRegion *r) {
// region 必须是大对象
if (!r->is_starts_humongous()) {
return false;
}
// 堆对象
G1CollectedHeap *g1h = G1CollectedHeap::heap();
// 是否是一个回收候选的 region
bool is_candidate = humongous_region_is_candidate(g1h, r);
// 这个 region 在 heap 中的索引
uint rindex = r->hrm_index();
// 在 heap 中标记这个 region 是一个回收候选 region
g1h->set_humongous_reclaim_candidate(rindex, is_candidate);
if (is_candidate) {
// 候选次数 +1
_candidate_humongous++;
// 把当前 region 放入一个集合,这个集合用于快速测试是否有引用指向了集合内元素
g1h->register_humongous_region_with_cset(rindex);
if (!r->rem_set()->is_empty()) {
// 转移用的记忆集不空
// 以下是 heap 管理的卡表
G1CardTable *ct = g1h->card_table();
// 迭代器,遍历的是当前 region 的 remSet 中记录的 other region 里的卡表
HeapRegionRemSetIterator hrrs(r->rem_set());
size_t card_index;
// 遍历卡表里的每个元素
while (hrrs.has_next(card_index)) {
// card_ptr 是卡表指定元素的指针,指向索引为 card_index 的这个元素
jbyte *card_ptr = (jbyte *) ct->byte_for_index(card_index);
// card_ptr 指向的位置必须在已使用空间范围内
if (g1h->is_in_closed_subset(ct->addr_for(card_ptr))) {
// 卡表元素的值没有标脏
if (*card_ptr != G1CardTable::dirty_card_val()) {
// 进行标脏
*card_ptr = G1CardTable::dirty_card_val();
// 入队到脏卡表队列中
_dcq.enqueue(card_ptr);
}
}
}
// 清理锁信息
r->rem_set()->clear_locked(true /* only_cardset */);
// 标记卡表处理完成
r->rem_set()->set_state_complete();
}
}
// 统计大 region 的个数
_total_humongous++;
return false;
}
};class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
bool humongous_region_is_candidate(G1CollectedHeap *g1h, HeapRegion *region) const {
// 大对象 region 里面只有一个对象,所以 region 的底部地址就是对象的首地址
oop obj = oop(region->bottom());
// 对象已经死亡就不管了
if (g1h->is_obj_dead(obj, region)) {
return false;
}
// remember set 是否已经更新完成,没有更新完成会返回 false
if (!region->rem_set()->is_complete()) {
return false;
}
// 对象是数组,且这个 region 是否是一个潜在的急切需要回收的候选 region
return obj->is_typeArray() &&
g1h->is_potential_eager_reclaim_candidate(region);
}
};相关代码
bool G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
// 代码行数 2952
evacuate_collection_set(&per_thread_states);
}void G1CollectedHeap::evacuate_collection_set(G1ParScanThreadStateSet *per_thread_states) {
// 已激活 worker 线程数量
const uint n_workers = workers()->active_workers();
// 根遍历的方法
G1RootProcessor root_processor(this, n_workers);
// 封装一个并行执行任务
G1ParTask g1_par_task(this, per_thread_states, _task_queues, &root_processor, n_workers);
// 开始同步执行
workers()->run_task(&g1_par_task);
}相关代码
class G1ParTask : public AbstractGangTask {
protected:
G1RootProcessor* _root_processor; // 通过构造函数传入
void work(uint worker_id) {
_root_processor->evacuate_roots(pss, worker_id);
}
};void G1RootProcessor::evacuate_roots(G1ParScanThreadState* pss, uint worker_i) {
G1EvacuationRootClosures *closures = pss->closures();
// 遍历java 线程的根,和 VMThrea的 线程的根
process_java_roots(closures, phase_times, worker_i);
// 结合上下文,这里是 false
if (closures->trace_metadata()) {
worker_has_discovered_all_strong_classes();
}
// 看不懂的一些根
process_vm_roots(closures, phase_times, worker_i);
// 字符串常量池的根
process_string_table_roots(closures, phase_times, worker_i);
if (!_process_strong_tasks.is_task_claimed(G1RP_PS_refProcessor_oops_do)) {
// 将并发标记的存活列表视为根
_g1h->ref_processor_cm()->weak_oops_do(closures->strong_oops());
}
if (closures->trace_metadata()) {
{
// 等待强引用检索完毕
wait_until_all_strong_classes_discovered();
}
// 类加载器的根
ClassLoaderDataGraph::roots_cld_do(NULL, closures->second_pass_weak_clds());
}
if (!_process_strong_tasks.is_task_claimed(G1RP_PS_filter_satb_buffers) &&
_g1h->collector_state()->mark_or_rebuild_in_progress()) {
// 并发标记期间做一些处理
G1BarrierSet::satb_mark_queue_set().filter_thread_buffers();
}
// 本线程处理完毕,并修改完成状态
_process_strong_tasks.all_tasks_completed(n_workers());
}