G1 Young GC 之收尾阶段
- 总览
- G1ClearCardTableTask
- RestoreEvacFailureRegionsTask
- RestorePreservedMarksTask
- ProcessEvacuationFailedRegionsTask
- RedirtyLoggedCardsTask
- FreeCollectionSetTask
- 总结
前文 GC 复制阶段已经将所有的对象从 cset 复制到其他 region,本文收尾阶段将对一些遗留问题进行处理。收尾阶段任务很多,仅讲解一些与前文相关性比较大的任务。
总览
` allocator()->release_gc_alloc_regions` 释放对象复制时使用的 region。
post_evacuate_cleanup_1
任务集合1。
post_evacuate_cleanup_2
任务集合2。
_evac_failure_regions.post_collection
重置记录失败信息的数据结构。
rebuild_free_region_list
重新构造空闲 region 列表。
prepare_for_mutator_after_young_collection
为 Java 线程活动准备 region,并将 Survivor 区域加入到回收集。
void G1YoungCollector::post_evacuate_collection_set(G1EvacInfo* evacuation_info, G1ParScanThreadStateSet* per_thread_states) {
//omit process weak reference process
allocator()->release_gc_alloc_regions(evacuation_info);
post_evacuate_cleanup_1(per_thread_states);
post_evacuate_cleanup_2(per_thread_states, evacuation_info);
//concurrent gc
if (collector_state()->in_concurrent_start_gc()) {
enqueue_candidates_as_root_regions();
}
_evac_failure_regions.post_collection();
_g1h->rebuild_free_region_list();
_g1h->prepare_for_mutator_after_young_collection();
_g1h->expand_heap_after_young_collection();
}
G1ClearCardTableTask
此任务是清空部分 region 的 card table。
add_all_dirty_region
在前面文章说过代表的是 cset 所有 region。由于 cset 中的对象都被清空,那么就不存在跨 region 指针,对应的 card table 区域也需要被清空。
r->clear_cardtable()
执行清空任务。
new G1ClearCardTableTask(G1CollectedHeap::heap(), _all_dirty_regions, this);
void do_work(uint worker_id) override {
const uint num_regions_per_worker = num_cards_per_worker / (uint)G1HeapRegion::CardsPerRegion;
while (_cur_dirty_regions < _regions->size()) {
uint next = Atomic::fetch_then_add(&_cur_dirty_regions, num_regions_per_worker);
uint max = MIN2(next + num_regions_per_worker, _regions->size());
for (uint i = next; i < max; i++) {
G1HeapRegion* r = _g1h->region_at(_regions->at(i));
r->clear_cardtable();
}
}
}
RestoreEvacFailureRegionsTask
此任务处理复制失败的 region,也就说 region 上还有对象没有被转移,需要做特殊处理。
_chunk_bitmap
用于记录被遍历的内存块。
claim_chunk
用于同步工作线程。
zap_dead_objects
填充活对象之间的区域。
class G1PostEvacuateCollectionSetCleanupTask1::RestoreEvacFailureRegionsTask{
CHeapBitMap _chunk_bitmap;
void do_work(uint worker_id) override {
for (uint i = 0; i < total_chunks; i++) {
const uint chunk_idx = (start_chunk_idx + i) % total_chunks;
if (claim_chunk(chunk_idx)) {
process_chunk(worker_id, chunk_idx);
} } }
}
void process_chunk(uint worker_id, uint chunk_idx) {
//omit
HeapWord* first_marked_addr = bitmap->get_next_marked_addr(chunk_start, hr_top);
HeapWord* obj_addr = first_marked_addr;
do {
oop obj = cast_to_oop(obj_addr);
const size_t obj_size = obj->size();
HeapWord* const obj_end_addr = obj_addr + obj_size;
obj->init_mark();
hr->update_bot_for_block(obj_addr, obj_end_addr);
HeapWord* next_marked_obj_addr = bitmap->get_next_marked_addr(obj_end_addr, hr_top);
garbage_words += zap_dead_objects(hr, obj_end_addr, next_marked_obj_addr);
obj_addr = next_marked_obj_addr;
} while (obj_addr < chunk_end);
}
RestorePreservedMarksTask
对于转移失败的对象,并且 markword 被存储的需要重新恢复。
class G1PostEvacuateCollectionSetCleanupTask2::RestorePreservedMarksTask : public G1AbstractSubTask {
WorkerTask* _task;
public:
RestorePreservedMarksTask(PreservedMarksSet* preserved_marks) :
_task(preserved_marks->create_task()) { }
void do_work(uint worker_id) override { _task->work(worker_id); }
};
WorkerTask* PreservedMarksSet::create_task() {
return new RestorePreservedMarksTask(this);
}
//_preserved_marks_set->get(task_id)->restore_and_increment(&_total_size);
//restore()
void PreservedMarks::restore() {
while (!_stack.is_empty()) {
const PreservedMark elem = _stack.pop();
elem.set_mark(); //_o->set_mark(_m);
}
assert_empty();
}
ProcessEvacuationFailedRegionsTask
对于在 cset 中, 还存在对象的region,看情况在 Young GC 阶段清空 mark bit。
bool clear_mark_data = !g1h->collector_state()->in_concurrent_start_gc() ||
g1h->policy()->should_retain_evac_failed_region(r);
if (clear_mark_data) {
g1h->clear_bitmap_for_region(r);
} else {
// This evacuation failed region is going to be marked through. Update mark data.
cm->update_top_at_mark_start(r);
cm->set_live_bytes(r->hrm_index(), r->live_bytes());
assert(cm->mark_bitmap()->get_next_marked_addr(r->bottom(), cm->top_at_mark_start(r)) != cm->top_at_mark_start(r),
"Marks must be on bitmap for region %u", r->hrm_index());
}
RedirtyLoggedCardsTask
处理 dirty card 任务队列,处理两类 region:
- 不在 cset 中。
- 在 cset 中,但是还有对象未移动。
void do_card_ptr(CardValue* card_ptr) override {
G1HeapRegion* hr = region_for_card(card_ptr);
// Should only dirty cards in regions that won't be freed.
if (!will_become_free(hr)) {
*card_ptr = G1CardTable::dirty_card_val();
_num_dirtied++;
}
}
bool will_become_free(G1HeapRegion* hr) const {
// A region will be freed by during the FreeCollectionSet phase if the region is in the
// collection set and has not had an evacuation failure.
return _g1h->is_in_cset(hr) && !_evac_failure_regions->contains(hr->hrm_index());
}
FreeCollectionSetTask
释放 cset,对于还有对象没有处理的 region ,如果存活对象小于某个阈值则需要加入到 cset 候选中,反之不处理。
对于清空的 reigon 则加入 free_list
中。
virtual bool do_heap_region(G1HeapRegion* r) {
if (_evac_failure_regions->contains(r->hrm_index())) {
handle_failed_region(r);
} else {
handle_evacuated_region(r);
}
}
总结
本文简单的介绍了 G1 的收尾阶段,对其中感兴趣的内容读者可自行深入阅读源码。