From af08cac97cb6ebe1017520422bb03eb37feedfde Mon Sep 17 00:00:00 2001 From: yoa1226 Date: Tue, 22 Oct 2024 15:47:31 +0800 Subject: [PATCH] ygc post --- _posts/2024-09-05-g1-gc-young-gc-post.md | 189 ++++++++++++++++++- _posts/2024-09-10-g1-gc-young-gc-iter-obj.md | 6 + 2 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 _posts/2024-09-10-g1-gc-young-gc-iter-obj.md diff --git a/_posts/2024-09-05-g1-gc-young-gc-post.md b/_posts/2024-09-05-g1-gc-young-gc-post.md index c16882b..2920ef6 100644 --- a/_posts/2024-09-05-g1-gc-young-gc-post.md +++ b/_posts/2024-09-05-g1-gc-young-gc-post.md @@ -1,6 +1,191 @@ --- layout: post -title: "G1 Young GC 之清理阶段" +title: "G1 Young GC 之收尾阶段" date: 2024-09-05 11:00:00 +0200 tags: [GC, G1] ---- \ No newline at end of file +--- + +前文 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 区域加入到回收集。 + +```cpp +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()` 执行清空任务。 + +```cpp +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` 填充活对象之间的区域。 + +```cpp +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 被存储的需要重新恢复。 + +```cpp + +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 + +在 Young GC 阶段清空 markbit。 + +## RedirtyLoggedCardsTask + +处理 dirty card 任务对象,除了在 cset 中,并且没有对象存在的 reigon 都会被处理 + +```cpp +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` 中。 + +```cpp +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 的收尾阶段,对其中感兴趣的内容读者可自行深入阅读源码。 \ No newline at end of file diff --git a/_posts/2024-09-10-g1-gc-young-gc-iter-obj.md b/_posts/2024-09-10-g1-gc-young-gc-iter-obj.md new file mode 100644 index 0000000..ad2aba9 --- /dev/null +++ b/_posts/2024-09-10-g1-gc-young-gc-iter-obj.md @@ -0,0 +1,6 @@ +--- +layout: post +title: "JVM 对象遍历" +date: 2024-09-05 11:00:00 +0200 +tags: [GC, G1] +--- \ No newline at end of file