Bootstrap

Minor GC、Major GC、Full GC(详解)

JVM在进行GC时,并非每次都对三个内存(新生代、老年代;方法区)区域一起回收的,大部分时候回收的都是指新生代。

针对HotSpot VM的实现,它里面的GC按照回收区域又分为两大种类型:一种是部分收集(Partial GC),一种是整堆回收(Full GC)

部分收集: 不是完整收集整个Java堆的垃圾收集。其中又分为:

新生代收集(Minor GC / Young GC): 只是新生代(Eden \ S0,S1)的垃圾收集

老年代收集(Major GC / Old GC): 只是老年代的垃圾收集
✔目前,只有CMS GC会有单独收集老年代的行为。
✔注意,很多时候Major GC会和Full GC混淆使用,需要具体分辨是老年代回收还是整堆回收。

混合收集(Mixed GC): 收集整个新生代以及部分老年代的垃圾收集
✔目前,只有G1 GC会有这种行为

整堆收集(Full GC): 收集整个Java堆和方法区的垃圾收集

年轻代GC(Minor GC)触发机制:

当年轻代空间不足时,就会触发Minor GC,这里的年轻代满指的是Eden代满,Survivor满不会引发GC。(每次 Minor GC 会清理年轻代的内存。)
因为Java对象大多都具备朝生夕灭的特性,所以Minor GC 非常频繁,一般回收速度也比较快。这一定义既清晰又易于理解。
Minor GC会引发STW,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行。
在这里插入图片描述
老年代GC(Major GC / Full GC)触发机制:

指发生在老年代的GC,对象从老年代消失时,我们说“Major GC”或“Full GC”发生了。

出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。
✔也就是在老年代空间不足时,会先尝试触发Minor GC。如果之后空间还不足,则触发Major GC

Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。

如果Major GC 后,内存还不足,就报OOM了。

Full GC 触发机制:
触发Full GC 执行的情况有如下五种:

调用System.gc()时,系统建议执行Full GC,但是不必然执行
老年代空间不足
方法区空间不足
通过Minor GC后进入老年代的平均大小大于老年代的可用内存
有Edan区、survivor space0(From Space)区向survivor space1(To Space)区复制时,对象大小大于To Space可用内存,则把对象转存到老年代,且老年代的可用内存小于该对象大小。

1、System.gc

2、promotion failed (年代晋升失败,比如eden区的存活对象晋升到S区放不下,又尝试直接晋升到Old区又放不下,那么Promotion Failed,会触发FullGC)

3、CMS的Concurrent-Mode-Failure 由于CMS回收过程中主要分为四步:
1.CMS initial mark 2.CMS Concurrent mark 3.CMS remark 4.CMS Concurrent sweep。

在2中gc线程与用户线程同时执行,那么用户线程依旧可能同时产生垃圾,
如果这个垃圾较多无法放入预留的空间就会产生CMS-Mode-Failure,
切换为SerialOld单线程做mark-sweep-compact。

4、新生代晋升的平均大小 大于 老年代的剩余空间 (为了避免新生代晋升到老年代失败)

当使用G1,CMS 时,FullGC发生的时候 是 Serial+SerialOld。
当使用ParalOld时,FullGC发生的时候是 ParallNew +ParallOld.

Cms退化为serial gc的情况

Minor GC后存活的对象晋升到老年代时由于悲观策略的原因,有两种情况会触发Full GC,:

1.之前每次晋升的对象的平均大小 > 老年代剩余空间;

2.Minor GC后存活的对象超过了老年代剩余空间。

这两种情况都是因为老年代会为新生代对象的晋升提供担保,而每次晋升的对象的大小是无法预测的,所以只能基于统计,1个是基于历史平均水平,一个是基于下一次可能要晋升的最大水平。这两种情况都是属于promotion failure。

CMS失败,发生concurrent mode failure会引起Full GC,这种情况下会使用Serial Old收集器,是单线程的,对GC的影响很大。concurrent mode failure产生的原因是老年代剩余的空间不够,导致了和gc线程并发执行的用户线程创建的大对象(由PretenureSizeThreshold控制新生代直接晋升老年代的对象size阀值)不能进入到老年代,只要stop the world来暂停用户线程,执行GC清理。可以通过设置CMSInitiatingOccupancyFraction预留合适的CMS执行时剩余的空间。

新生代直接晋升到老年代的大对象超过了老年代的剩余空间,引发Full GC。

注意于promotion failure的区别,promotion failure指的是Minor GC后发生的担保失败。

参考

https://blog.csdn.net/weixin_45101064/article/details/123477923?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-123477923-blog-107011297.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-123477923-blog-107011297.pc_relevant_recovery_v2&utm_relevant_index=1

;