细节炸裂!阿里大佬深入探究G1源码之YoungGC技术

笔者经过上次研究ZGC的代码之后,感受到了ZGC性能的提升和设计的巧妙,由此知道了ZGC的各种优势。但是现在日常生产中许多应用还是用的JAVA8和G1,作为一个对技术有追求的程序员,不由得产生了看一看G1源码的想法。笔者一直本着比较学习才能收获更多的原则,所以打开了java8的源码,开始学习G1,在学习过程中发现,G1的代码相较于zgc阅读起来稍微有些困难,花的时间更长一些。网上许多关于G1源码的文章并不是很全面,有些问题也没有讲清楚,笔者会尽量将源码的细节阐述清楚。

首先G1的GC过程分为4个阶段或者说策略:

1.youngGC

2.老年代并发GC(又叫并发模式gc,本文都先称之为老年代并发gc)

3.混合GC

4.FullGC

每个阶段GC的内容都比较多,在这里由于篇幅原因,我们先讲解下g1中youngGc(源码中也叫增量gc) 的源码,话不多说,直接来看:

一、youngGC触发的时机

关于GC笔者不建议直接去看GC的方法,最好是从GC触发的时机去看,从入口去看,这样才能更全面的学习GC的源码,更深层次的学习GC的相关流程。

youngGC的触发时机是在创建对象时若申请不到内存,则会触发一次youngGC,说起来很简单,我们看下源码,其实还是有很多细节:

 
 

<code>//熟悉jvm源码的朋友都知道IRT_ENTRY是运行解释器的宏,其实是定义了一个_new方法 //这里我们可以理解成是解释java new关键字的方法,本质上在java使用new关键字时会调用这个方法 IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index)) //先会去常量池(这里的常量池其实运行时常量池)中查找Klass的信息 Klass* k_oop = pool->klass_at(index, CHECK); //然后将其包装成instanceKlassHandle句柄,其实就是klass包装类 instanceKlassHandle klass (THREAD, k_oop); //做一些验证和初始化操作 klass->check_valid_for_instantiation(true, CHECK); klass->initialize(CHECK); //调用申请对象的方法 oop obj = klass->allocate_instance(CHECK); //将申请的结果返回 thread->set_vm_result(obj); IRT_END </code><button>复制</button>

之后会跳到这个方法 InstanceKlass::allocate_instance:

 
 

<code>//这个方法返回的instanceOop是instanceOopDesc指针的别名(不开启预编译),instanceOopDesc是oopDesc //的子类,表示java class的实例 //InstanceKlass是Klass的子类,表示类的元数据 instanceOop InstanceKlass::allocate_instance(TRAPS) { //判断是否定义finalizer方法 bool has_finalizer_flag = has_finalizer(); //返回实例大小 int size = size_helper(); //封装成KlassHandle句柄,可以简单理解为是Klass的封装类 KlassHandle h_k(THREAD, this); instanceOop i; //创建对象实例 i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL); //注册finalizer方法 if (has_finalizer_flag && !RegisterFinalizersAtInit) { i = register_finalizer(i, CHECK_NULL); } return i; } </code><button>复制</button>

看到这里,小伙伴们可能会有点迷惑,搞不懂instanceOopDesc和InstanceKlass,这个地方涉及到jvm的oop-klass模型,我们来画张图简单说明下,通过这张图,我们就可以理解这两句话的原因:

1.加载.class到元空间时,会在堆中实例化出一个class对象

实际上是通过instanceMirrorKlass实例化出的

2.在java中利用反射传入的是java.lang.Class对象,创建出的是普通的java实例

因为java.lang.Class对象实际上是InstanceKlass的java镜像类,可以通过java.lang.Class对象获取InstanceKlass的信息,进而获取普通java实例的信息,从而创建普通实例

编辑

添加图片注释,不超过 140 字(可选)

关于oop-klass模型也非常有意思,有兴趣的读者可以自行查看下源码,本篇还是着重讲述youngGC

言归正传,我们继续往下看:

 
 

oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) { //申请内存并初始化对象 HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL); post_allocation_setup_obj(klass, obj); return (oop)obj; } HeapWord* CollectedHeap::common_mem_allocate_init(KlassHandle klass, size_t size, TRAPS) { //先申请内存 HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL); init_obj(obj, size); return obj; } //最后进入这个方法 HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) { HeapWord* result = NULL; //是否使用TLAB处理,这里先不论述TLAB我们直接跳过 if (UseTLAB) { result = allocate_from_tlab(klass, THREAD, size); if (result != NULL) { return result; } } bool gc_overhead_limit_was_exceeded = false; //堆中申请内存,直接进入这个方法 result = Universe::heap()->mem_allocate(size, &gc_overhead_limit_was_exceeded); ...... } 复制

Universe::heap()这个方法返回的是当前jvm使用的堆类,由于我们是G1的垃圾回收器,所以这里返回的是g1CollectedHeap.cpp:

 
 

HeapWord* G1CollectedHeap::mem_allocate(size_t word_size, bool* gc_overhead_limit_was_exceeded) { //循环直到申请成功或者GC后申请失败 for (int try_count = 1, gclocker_retry_count = 0; /* we'll return */; try_count += 1) { unsigned int gc_count_before; HeapWord* result = NULL; //判断是否是大对象 if (!isHumongous(word_size)) { //申请大对象的情况相对来说比较复杂,我们这里先看下申请小对象的逻辑 result = attempt_allocation(word_size, &gc_count_before, &gclocker_retry_count); } else { result = attempt_allocation_humongous(word_size, &gc_count_before, &gclocker_retry_count); } if (result != NULL) { return result; } // 这个是fullGC的操作任务类 VM_G1CollectForAllocation op(gc_count_before, word_size); //这里申请失败则会执行fullGC(这里先不论述FullGC) VMThread::execute(&op); ...... } return NULL; } 复制

大对象的申请流程比较复杂,笔者这里简单画了张图表示下大对象申请的流程,小伙伴们可以参考了解下,当然其中具体的步骤肯定不止这么简单,有兴趣的小伙伴可以自行查看源码

编辑

添加图片注释,不超过 140 字(可选)

从小对象方法申请入手我们继续看:

 
 

inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size, unsigned int* gc_count_before_ret, int* gclocker_retry_count_ret) { //再去申请一次内存,_mutator_alloc_region内部有指向当前活跃的eden region,每次申请会从这里申请内存 //在每次gc之前会清空申请的内存 HeapWord* result = _mutator_alloc_region.attempt_allocation(word_size, false /* bot_updates */); if (result == NULL) { //失败后进入这个方法 result = attempt_allocation_slow(word_size, gc_count_before_ret, gclocker_retry_count_ret); } if (result != NULL) { dirty_young_block(result, word_size); } return result; } //进入这个方法 HeapWord* G1CollectedHeap::attempt_allocation_slow(size_t word_size, unsigned int *gc_count_before_ret, int* gclocker_retry_count_ret) { //这里默认情况下会循环三次,根据这个参数GCLockerRetryAllocationCount HeapWord* result

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值