JVM基础原理篇-带你深入拆解G1垃圾回收原理

一、一统天下的G1垃圾回收器概述

大白话:

        1.整个堆空间,新生代和老年代比例大概为2:8;

        2.正常情况下,新生代回收是高频的,混合回收是频率是适中的,完全回收则是基本不会发生、频率低代价高的,一旦发生距离系统崩溃不远矣。

大白话:

        1.Eden区不够了,触发YGC;

        2.新生代中的对象不断地晋升(满15次)老年代,当老年代达到阈值,则先触发混合回收,将新生代和部分老年代进行垃圾回收;

        3.如果回收后,老年代对象仍不够用,则会触发完全回收(FullGC);

        4.多次FullGC失败后,抛出OOM。

二、图解G1垃圾回收器YGC的基本原理与执行过程

大白话:

        1.方法区(元空间)所关联的类、对象,方法区里的东西一般是常驻内存的;

        2.正在执行的线程关联的类、对象,是不会被标记清除的;

        3.跨代引用,老年代引用了新生代对象,这些新生代对象也是不会被清除的;

这些对象都是可以作为GC root起点的。

大白话:

        我们通过对CMS的执行过程理解,首先是初始标记、并发标记、预处理、重新标记、并发清理、重置线程,而G1在标记为存活对象后,就立刻移动该对象到S区。

大白话:

        动态调整新生代区域region的数量 - 在G1里,在执行YGC的时候,新生代的数量是可以动态变化的,一般什么都不设置的情况下,Eden区占比例在5%~60%之间(调整的原因,是根据每次执行垃圾回收后,根据当前的情况判断增加region数量,每增加一个region数量,Eden区就会增加一个,如果没有必要,Eden数量太多,导致回收时间太长,这时JVM就会减少Eden区数量)。

大白话:

        当区块被回收后,就会变成自由分区(或者叫空白分区),再有G1根据情况决定,这个区块下一步分变为Eden区还是别的区块。

大白话:

        在G1不断地YGC后,当某一次YGC判断老年代空间可能不太够用,则为下一次可能执行的混合回收做准备,提前开启并发标记,提高回收效率。

大白话:

        并行执行的任务,一般是重要的,且耗时短的。

大白话:

        1.释放分区 - 释放分区,类似我们的格式化硬盘,工作比较简单也比较快,涉及到一些全局状态表、全局集合的调整,所以采用串行的方式比较好。

三、【场景演示】通过GC日子理解YGC基本过程

大白话:

        young - YGC

        GC Workers - 8个工作线程

        Ext Root Scanning - GCRoots扫描

注意

        [Ext Root Scanning (ms):  0.4  0.6  0.2  0.2  0.2  0.1  0.2  0.1,从这里可以看出,不同的线程消耗的时间为什么会差异这么大,是因为不同的CPU的工作任务不同。

大白话: 

        total - G1堆总大小;

        used - 已使用堆大小;

        region size - 堆分区大小;

        28 young(28672k) - 年轻代分区有28个,总大小28672k;

        1 survivors(1024k) - S区1个,总大小1024k;

[Eden:9216.0K(9216.0K)垃圾回收之前占用空间大小 -> 0.0B垃圾回收之后占用空间大小(31.0M Eden区对占用空间大小 Survivors:0.0B -> 1024.0K Heap:9216.0K(128.0M) -> 848.1K(128.0M))]

再执行一次程序代码,发现

通过这两次对比,可以看出Eden区的堆空间大小被调整了,且Eden区的堆大小在5%~60%之间。

四、【原理精讲】停顿预测模型与垃圾区域选择原理

在启动参数中,增加一个 -XX:MaxGCPauseMillis=1 (最大停顿时间1,单位为1毫秒)

发现进行了多次GC,原因是每次GC都会尽量满足最大挺短时间,导出的后果就是每次垃圾会输的时间缩短了。

因为设置了最大停顿时间,导致GC时间内能处理的东西减少 ,从而JVM自动缩小了Eden区的堆大小。

大白话:

        最大停顿时间,设置过小,导致GC时间内能处理的东西减少 ,从而JVM自动缩小了Eden区的堆大小,设置过大,则导致在YGC的时候,S区放不下

大白话:

        根据垃圾对象占对象的比例排序,如果本次能清理3个region,就1/2/3一起清理,如果只能清理2个就清理1/2,如果 只能清理1个region,就只清理1号region,选出来的region,放到CSet中。

五、【原理精讲】深入理解混合回收工作原理与执行过程

因为并发标记阶段耗时非常长,有些对象是死的,被标记成了活动,有些对象是活的被标记成了死的。

大白话:

        这里就是前面讲到的,怎么统计每个region的垃圾笔记、以及region回收排序。

大白话:

        1.混合回收前面为什么会有一次YGC呢,前面讲了混合回收的触发条件是,在YGC后,已分配的内存占总内存的45%,触发混合回收;

        2.并发标记,标记的什么呢,主要标记的是老年代的对象。

大白话:

        为什么存活对象数量大于 85%的region就不再回收,这是因为前面讲了,一边标记一边复制,最耗时的是复制移动对象,所以一旦超过85%的region,就没有放入CSet的中回收的必要。

大白话:

        也就说,要执行混合有两个条件,第一个,在YGC后,已分配的内存占总内存的45%,触发混合回收,第二个就是可回收的空间占总空间的比例大于5%,才会启动混合回收。如果低于5%,JVM认为启动混合回收的意义不大,所以不启动混合回收。

五、【场景演示】通过GC日志理解混合回收工作过程

将for循环改为500次:

通过检查GC日志,发现这一个YGC跟前面的YGC已经有了明显的区别,多做了一步初始标记操作,那么后面离它不远,就会有混合回收日志。

这里已经开始并发标记

开始混合回收

这一行表示,老年代空间已经不够使用,G1通过把新生代快速转为老年代,供老年代使用,出现这一行,表示离FullGC不远矣。

触发了2次FullGC

在FullGC后,又是YGC,说明暂时把系统救回来了

到最后,FullGC已经解决不了问题了,最终OOM了。

六、【原理精讲】深入理解FullGC工作原理与执行过程

大白话:

        FullGC执行时间非常长,大概2-3秒,如果执行一次仍然不够,会再次执行,第二次回收软引用,如果还不够用,对象仍然无法分配,系统基本上就要OOM了,所以FullGC是非常危险的,当出现FullGC,我们就应该进行检查,防止更大的风险发生。

大白话:

        标记活跃对象,是所有垃圾回收期的回收的前提。

七、【故障实战】大对象导致视频无法观看故障实战

大白话:

        经验证,G1采用的是第二种思路(即使用多个region)存放大对象。

        但是有个基本原则,大对象尽量不要放入老年代,因为老年代region是有数量限制的(默认2048个),如果存放大量大对象,会把老年代占满,一旦占满应用会非常容易崩掉。

问题:

        现在的手机拍摄的视频一般都几百兆甚至几个G。

大白话:

        手动加大region大小,这个时候,大对象就不会被视为大对象,也就不会直接放入老年代region。

在前边的代码里,增加设置-XX:G1HeapRegionSize=8M,加入region大小。同时将代码中的byte1024改为1022,让数据的大小变小,可以看到执行的是YGC。

八、为什么sublist会导致系统OOM?

原因:

        subList所在的大对象,只要一直有在被引用,就一直不会被回收,而其本身占用的堆内存也一直不会释放,所以不推荐使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值