1. 简介

当晋升失败、疏散失败、大对象分配失败、Evac失败时,有可能触发Full GC,在JDK10之前,Full GC是串行的,JEP 307: Parallel Full GC for G1之后引入了并行Full GC。本文主要介绍并行Full GC机制。

2. 源码分析

Full GC的入口在g1CollectedHeap.cpp的G1CollectedHeap::do_full_collection

bool G1CollectedHeap::do_full_collection(bool explicit_gc,bool clear_all_soft_refs) {assert_at_safepoint_on_vm_thread();if (GCLocker::check_active_before_gc()) {// Full GC was not completed.return false;}const bool do_clear_all_soft_refs = clear_all_soft_refs ||soft_ref_policy()->should_clear_all_soft_refs();G1FullCollector collector(this, explicit_gc, do_clear_all_soft_refs);GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true);collector.prepare_collection();collector.collect();collector.complete_collection();// Full collection was successfully completed.return true;
}
  • 准备回收,prepare_collection
  • 回收,collect
  • 回收后处理,complete_collection

2.1 准备阶段

void G1FullCollector::prepare_collection() {_heap->g1_policy()->record_full_collection_start();_heap->print_heap_before_gc();_heap->print_heap_regions();_heap->abort_concurrent_cycle();_heap->verify_before_full_collection(scope()->is_explicit_gc());_heap->gc_prologue(true);_heap->prepare_heap_for_full_collection();reference_processor()->enable_discovery();reference_processor()->setup_policy(scope()->should_clear_soft_refs());// When collecting the permanent generation Method*s may be moving,// so we either have to flush all bcp data or convert it into bci.CodeCache::gc_prologue();// We should save the marks of the currently locked biased monitors.// The marking doesn't preserve the marks of biased objects.BiasedLocking::preserve_marks();// Clear and activate derived pointer collection.clear_and_activate_derived_pointers();
}
  • Full GC应当清理软引用
  • 由于Full GC过程中,永久代(元空间)中的方法可能被移动,需要保存bcp字节码指针数据或者转化为bci字节码索引
  • 保存轻量级锁和重量级锁的对象头
  • 清理和处理对象的派生关系

2.2 回收阶段

void G1FullCollector::collect() {phase1_mark_live_objects();verify_after_marking();// Don't add any more derived pointers during later phasesdeactivate_derived_pointers();phase2_prepare_compaction();phase3_adjust_pointers();phase4_do_compaction();
}
  • phase1 并行标记对象
  • phase2 并行准备压缩
  • phase3 并行调整指针
  • phase4 并行压缩

2.2.1 并行标记

从GC roots出发,递归标记所有的活跃对象。

void G1FullCollector::phase1_mark_live_objects() {// Recursively traverse all live objects and mark them.GCTraceTime(Info, gc, phases) info("Phase 1: Mark live objects", scope()->timer());// Do the actual marking.G1FullGCMarkTask marking_task(this);run_task(&marking_task);// Process references discovered during marking.G1FullGCReferenceProcessingExecutor reference_processing(this);reference_processing.execute(scope()->timer(), scope()->tracer());// Weak oops cleanup.{GCTraceTime(Debug, gc, phases) debug("Phase 1: Weak Processing", scope()->timer());WeakProcessor::weak_oops_do(_heap->workers(), &_is_alive, &do_nothing_cl, 1);}// Class unloading and cleanup.if (ClassUnloading) {GCTraceTime(Debug, gc, phases) debug("Phase 1: Class Unloading and Cleanup", scope()->timer());// Unload classes and purge the SystemDictionary.bool purged_class = SystemDictionary::do_unloading(scope()->timer());_heap->complete_cleaning(&_is_alive, purged_class);} else {GCTraceTime(Debug, gc, phases) debug("Phase 1: String and Symbol Tables Cleanup", scope()->timer());// If no class unloading just clean out strings._heap->partial_cleaning(&_is_alive, true, G1StringDedup::is_enabled());}scope()->tracer()->report_object_count_after_gc(&_is_alive);
}
  • 标记对象,具体逻辑在G1FullGCMarkTask中
  • 清理弱引用
  • 卸载类的元数据(complete_cleaning)或仅清理字符串(partial_cleaning)
  • 清理字符串会清理StringTable和字符串去重(JEP 192: String Deduplication in G1)

G1FullGCMarkTask

void G1FullGCMarkTask::work(uint worker_id) {Ticks start = Ticks::now();ResourceMark rm;G1FullGCMarker* marker = collector()->marker(worker_id);MarkingCodeBlobClosure code_closure(marker->mark_closure(), !CodeBlobToOopClosure::FixRelocations);if (ClassUnloading) {_root_processor.process_strong_roots(marker->mark_closure(),marker->cld_closure(),&code_closure);} else {_root_processor.process_all_roots_no_string_table(marker->mark_closure(),marker->cld_closure(),&code_closure);}// Mark stack is populated, now process and drain it.marker->complete_marking(collector()->oop_queue_set(), collector()->array_queue_set(), _terminator.terminator());// This is the point where the entire marking should have completed.assert(marker->oop_stack()->is_empty(), "Marking should have completed");assert(marker->objarray_stack()->is_empty(), "Array marking should have completed");log_task("Marking task", worker_id, start);
}void G1RootProcessor::process_strong_roots(OopClosure* oops,CLDClosure* clds,CodeBlobClosure* blobs) {StrongRootsClosures closures(oops, clds, blobs);process_java_roots(&closures, NULL, 0);process_vm_roots(&closures, NULL, 0);_process_strong_tasks.all_tasks_completed(n_workers());
}void G1RootProcessor::process_all_roots(OopClosure* oops,CLDClosure* clds,CodeBlobClosure* blobs,bool process_string_table) {AllRootsClosures closures(oops, clds);process_java_roots(&closures, NULL, 0);process_vm_roots(&closures, NULL, 0);if (process_string_table) {process_string_table_roots(&closures, NULL, 0);}process_code_cache_roots(blobs, NULL, 0);_process_strong_tasks.all_tasks_completed(n_workers());
}void G1RootProcessor::process_all_roots(OopClosure* oops,CLDClosure* clds,CodeBlobClosure* blobs) {process_all_roots(oops, clds, blobs, true);
}void G1RootProcessor::process_all_roots_no_string_table(OopClosure* oops,CLDClosure* clds,CodeBlobClosure* blobs) {assert(!ClassUnloading, "Should only be used when class unloading is disabled");process_all_roots(oops, clds, blobs, false);
}
  • 如果允许卸载类的元数据,则调用process_strong_roots;否则调用process_all_roots_no_string_table
  • process_strong_roots的GC roots仅强根
  • process_all_roots_no_string_table的GC roots包括弱根、强根,但是不含StringTable
  • 遍历标记栈中的所有对象

2.2.2 准备压缩

计算每个活跃对象应该在什么位置,即计算对象压缩后的新位置指针并写入对象头。

void G1FullCollector::phase2_prepare_compaction() {GCTraceTime(Info, gc, phases) info("Phase 2: Prepare for compaction", scope()->timer());G1FullGCPrepareTask task(this);run_task(&task);// To avoid OOM when there is memory left.if (!task.has_freed_regions()) {task.prepare_serial_compaction();}
}
  • 调用G1FullGCPrepareTask准备压缩
  • 如果任务没有空闲Region,则调用prepare_serial_compaction串行合并所有线程的最后一个分区,以避免OOM

G1FullGCPrepareTask

void G1FullGCPrepareTask::work(uint worker_id) {Ticks start = Ticks::now();G1FullGCCompactionPoint* compaction_point = collector()->compaction_point(worker_id);G1CalculatePointersClosure closure(collector()->mark_bitmap(), compaction_point);G1CollectedHeap::heap()->heap_region_par_iterate_from_start(&closure, &_hrclaimer);// Update humongous region setsclosure.update_sets();compaction_point->update();// Check if any regions was freed by this worker and store in task.if (closure.freed_regions()) {set_freed_regions();}log_task("Prepare compaction task", worker_id, start);
}
  • 压缩对象具体逻辑在G1FullGCCompactionPoint中实现,执行完成后,对象头存储了对象的新地址
  • 如果是大对象分区,且对象已经都死亡,则直接释放分区

2.2.3 调整指针

在上一步计算出所有活跃对象的新位置后,需要修改引用到新地址。

void G1FullCollector::phase3_adjust_pointers() {// Adjust the pointers to reflect the new locationsGCTraceTime(Info, gc, phases) info("Phase 3: Adjust pointers", scope()->timer());G1FullGCAdjustTask task(this);run_task(&task);
}
void G1FullGCAdjustTask::work(uint worker_id) {Ticks start = Ticks::now();ResourceMark rm;// Adjust preserved marks first since they are not balanced.G1FullGCMarker* marker = collector()->marker(worker_id);marker->preserved_stack()->adjust_during_full_gc();// Adjust the weak roots.if (Atomic::add(1u, &_references_done) == 1u) { // First incr claims task.G1CollectedHeap::heap()->ref_processor_stw()->weak_oops_do(&_adjust);}AlwaysTrueClosure always_alive;_weak_proc_task.work(worker_id, &always_alive, &_adjust);CLDToOopClosure adjust_cld(&_adjust, ClassLoaderData::_claim_strong);CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);_root_processor.process_all_roots(&_adjust,&adjust_cld,&adjust_code);// Adjust string dedup if enabled.if (G1StringDedup::is_enabled()) {G1StringDedup::parallel_unlink(&_adjust_string_dedup, worker_id);}// Now adjust pointers region by regionG1AdjustRegionClosure blk(collector()->mark_bitmap(), worker_id);G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&blk, &_hrclaimer, worker_id);log_task("Adjust task", worker_id, start);
}
  • 调整之前保存的轻量级锁和重量级锁对象的引用地址
  • 调整弱根
  • 调整全部根对象
  • 处理字符串去重逻辑
  • 一个region一个region的调整引用地址

2.2.4 移动对象

对象的新地址和引用都已经更新,现在需要把对象移动到新位置

void G1FullCollector::phase4_do_compaction() {// Compact the heap using the compaction queues created in phase 2.GCTraceTime(Info, gc, phases) info("Phase 4: Compact heap", scope()->timer());G1FullGCCompactTask task(this);run_task(&task);// Serial compact to avoid OOM when very few free regions.if (serial_compaction_point()->has_regions()) {task.serial_compaction();}
}
  • 具体压缩对象逻辑在G1FullGCCompactTask
  • 如果phase2计算位置中使用了串行处理,则移动对象时也要使用串行处理移动每任务最后一个分区的对象

G1FullGCCompactTask

void G1FullGCCompactTask::work(uint worker_id) {Ticks start = Ticks::now();GrowableArray<HeapRegion*>* compaction_queue = collector()->compaction_point(worker_id)->regions();for (GrowableArrayIterator<HeapRegion*> it = compaction_queue->begin();it != compaction_queue->end();++it) {compact_region(*it);}G1ResetHumongousClosure hc(collector()->mark_bitmap());G1CollectedHeap::heap()->heap_region_par_iterate_from_worker_offset(&hc, &_claimer, worker_id);log_task("Compaction task", worker_id, start);
}size_t G1FullGCCompactTask::G1CompactRegionClosure::apply(oop obj) {size_t size = obj->size();HeapWord* destination = (HeapWord*)obj->forwardee();if (destination == NULL) {// Object not movingreturn size;}// copy object and reinit its markHeapWord* obj_addr = (HeapWord*) obj;assert(obj_addr != destination, "everything in this pass should be moving");Copy::aligned_conjoint_words(obj_addr, destination, size);oop(destination)->init_mark_raw();assert(oop(destination)->klass() != NULL, "should have a class");return size;
}
  • 迭代处理每个Region
  • 调用闭包G1CompactRegionClosure的apply函数移动对象到Region头部
  • 如果Region中的全部对象都已清理,则回收该Region

3. 引用

jdk12源代码[https://hg.openjdk.java.net/jdk/jdk12]
JEP-307[http://openjdk.java.net/jeps/307]
JEP-192[http://openjdk.java.net/jeps/192]

JVM G1 源码分析(七)- Full GC相关推荐

  1. TeamTalk客户端源码分析七

    TeamTalk客户端源码分析七 一,CBaseSocket类 二,select模型 三,样例分析:登录功能 上篇文章我们分析了network模块中的引用计数,智能锁,异步回调机制以及数据的序列化和反 ...

  2. JVM CMS 源码分析

    https://blog.csdn.net/weixin_45410925?t=1 为什么 Old Gen 使用占比仅 50% 就进行了一次 CMS GC? Metaspace 的使用也会触发 CMS ...

  3. 【转】ABP源码分析七:Setting 以及 Mail

    本文主要说明Setting的实现以及Mail这个功能模块如何使用Setting. 首先区分一下ABP中的Setting和Configuration. Setting一般用于需要通过外部配置文件(或数据 ...

  4. spring boot 源码分析(七) 事件机制 之 SpringApplicationEvent

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 前面的文章我们讲解了一下spring boot配置文件加载的相关源码分析,下面我们将从源码角度讲解一下spring  ...

  5. Spring Core Container 源码分析七:注册 Bean Definitions

    前言 原本以为,Spring 通过解析 bean 的配置,生成并注册 bean defintions 的过程不太复杂,比较简单,不用单独开辟一篇博文来讲述:但是当在分析前面两个章节有关 @Autowi ...

  6. Spring Security源码分析七:Spring Security 记住我

    有这样一个场景--有个用户初访并登录了你的网站,然而第二天他又来了,却必须再次登录.于是就有了"记住我"这样的功能来方便用户使用,然而有一件不言自明的事情,那就是这种认证状态的&q ...

  7. springfox源码_springfox 源码分析(七) 文档初始化

    时间:2019-5-23 20:12:04 地点:家中 通过前面几篇文章对springfox的介绍,以及我们的学习准备工作,这篇我们将正式来探索springfox是如何初始化的 我们在学算法的时候,其 ...

  8. rnnlm源码分析(七)

    系列前言 参考文献: RNNLM - Recurrent Neural Network  Language Modeling Toolkit(点此阅读) Recurrent neural networ ...

  9. 谷歌chrome浏览器的源码分析(七)

    上一次说到通过管道把接收到的HTTP数据通知另一个线程处理,它不是直接发送数据过去,而是把数据在共享内存里的句柄发送过去,达到高效通讯的目的.下面就来分析资源处理进程里,接收到这个消息之后,做些什么处 ...

最新文章

  1. SpringDataJpa根据多个id物品清单id查询房源编号
  2. WebRTC内置debug工具,详细参数解读
  3. 2017秋-软件工程第十二次作业(三)-版本控制报告(个人)
  4. 学python先学什么-学Python之前,什么最重要?
  5. 用python给女朋友惊喜100天快乐_100天从 Python 小白到大神最良心的学习资源!
  6. 矩阵连乘问题 Python 动态规划
  7. [你必须知道的.NET]第二十九回:.NET十年(上)
  8. JavaScript常用开发框架总结
  9. C++动态内存管理好难怎么办?零基础图文讲解,小白轻松理解原理
  10. 09年职称计算机考试,09年职称计算机考试试题A.doc
  11. java计算机毕业设计租车管理系统源码+mysql数据库+系统+部署+lw文档
  12. html字体版权,字体在网站中的版权问题
  13. 一、14.猜码游戏:每一轮里,程序随机生成两个数字,一个是码数,0到5,一个是猜数,码数到10。用户也输入码数和猜数。若这一轮程序的猜数等于两个码数之和,输出“电脑胜”,若都没猜对或都猜对了,公布双方
  14. 【文本】HTML5 Canvas小项目:为坐标轴添加数字标签(带刻度线)
  15. C语言入门(二)主菜单操作
  16. winmail 数据库设置_企业邮箱winmail设置方法
  17. 传智播客成都java培训中心的发展
  18. 按键脚本c语言,按键精灵脚本(示例代码)
  19. 以前写的网页游戏辅助工具源码 传奇类的HOOK 封包 按钮
  20. python输入函数是什么意思_新手学python之Python的输入输出函数

热门文章

  1. 混合高斯模型去除背景
  2. 遥感影像去背景 之 数据裁剪
  3. ubuntu 什么是xinetd
  4. 初识马尔可夫和马尔可夫链
  5. ios tableView那些事 (九) tableview的删除
  6. Windows远程桌面提示CredSSP加密数据库修正
  7. R语言数学建模(1):Regression analysis
  8. 【SRE笔记 2022.9.21 网络及TCP握手】
  9. Python与正则表达式
  10. 哈夫曼编码的简单实例