一、一统天下的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所在的大对象,只要一直有在被引用,就一直不会被回收,而其本身占用的堆内存也一直不会释放,所以不推荐使用。