JVM|6、JVM分代模型--老年代 的垃圾回收

老年代采用的是什么垃圾回收器算法? 老年代中的对象存活时间比较久,每次垃圾回收之后,存活对象比较多,而需要回收的垃圾对象比较少,所以适合采用标记-清除、标记-整理算法。无论是标记-清除还是标记-整理,都有一个阶段是清除垃圾对象,垃圾对象越少则执行效率越高。


对象是如何进入老年代的? 对象年龄够了之后:
前面讲过对象分配在新生代中之后,随着新生代的Eden区满了之后会进行新生代的垃圾回收,也就是Young GC,然后存活对象便流入Survivor1区,然后在Survivor1和Survivor2区中流转,每次Young GC之后存活的对象的年龄就增加1岁,默认当对象的年龄到15岁时,便可以进入老年代了。而具体是多少岁可以进入老年代,可以通过设置参数:-XX:MaxTenuringThreshold来设置。

动态对象年龄判断:
动态对象年龄判断就是说,年龄1+年龄2+…年龄n的所有对象占用的总内存超过了Survivor区的50%,此时就会把年龄n以上的对象都放入老年代。
其实就是说Young GC过后,存活的对象要放入其中一个Survivor区时,判断一下存活对象总和是否大于Survivor区的大小的50%,如果大于了,那么就找到年龄1的所有对象,看看大于还是小于Survivor的50%,如果是小于,就再加上年龄2的所有对象,再看看是大于还是小于Survivor的50%,一次类推,找到第一个大于Survivor的50%的那个年龄n,然后把所有年龄大于n的对象都放入老年代。
这个规则的意义在于:对于那些可能长期存活的对象来说,就尽早进入老年代,不用非要得到15岁之后,尤其是在Survivor区内存有压力的情况下。

大对象直接进入老年代:
如果跑程序的过程中创建了一个“大对象”,那么就不用经过新生代了,直接进入老年代。具体大于多少是“大对象”呢?可以通过参数: -XX:PretenureSizeThreshold来设置,单位为字节。
这样规则的意义在于:避免了大对象进入新生代,然后在Young GC过程中来回复制,降低复制效率。

老年代的空间分配担保机制:
如果Young GC之后,存活的对象太多,Survivor区放不下了,此时怎么办呢?那么此时这些存活对象会被放入老年代中去,也就是说由老年代进行空间分配担保。但随之而来的一个问题就是:如果老年代也放不下怎么办?JVM垃圾回收有一套详细的老年代空间分配担保机制,接下来我们好好看看:
先补充一下基础知识:老年代的垃圾回收叫Old GC,但是一般情况下,Old GC会伴随着新生代垃圾回收、永久代垃圾回收,所以也可以把老年代的垃圾回收叫做:Full GC,也就是全局垃圾回收。
1、Young GC之前是否需要Full GC 让老年代来腾点地方出来?
在执行Young GC之前,做一个判断:老年代可用空间大小是否大于新生代所有对象的总大小?因为极端情况下新生代所有对象全部存活下来也是有可能的。如果大于,那么可以放心地进行Young GC;如果小于,那么会做另外一个判断:查看-XX:HandlePromotionFailure 参数是否设置了?
1.1、如果没有设置,那么此时就会触发一次Full GC,让老年代先腾出一点空间,然后再进行Young GC;
1.2、如果设置了,那么再进行一个判断:老年代的可用内存大小是否大于之前每一次Young GC后进入老年代的对象的平均大小?
1.2.1、如果小于,那么此时还是会先触发一次Full GC,让老年代先腾出一点空间,然后再进行Young GC;
1.2.2、如果大于,那么可以尝试冒一点风险进行Young GC。
JVM|6、JVM分代模型--老年代 的垃圾回收
文章图片


2、进行Young GC,Young GC之后有以下几种可能:
【JVM|6、JVM分代模型--老年代 的垃圾回收】2.1:Young GC过后,剩余的存活对象大小,小于Survivor区大小,那么直接进入Survivor区;
2.2:Young GC过后,存活对象大小,大于Survivor区大小,但是小于老年代可用内存大小,此时进入老年代即可;
2.3:Young GC过后,存活对象大小,既大于Survivor区大小,也大于老年代可用内存大小,此时就会发生Handle Promotion Failure的情况,这时便会触发一次Full GC。

3、Full GC之后,如果老年代还是没有足够的空间来存放Young GC之后的对象,那么就会发生OOM,也就是内存溢出。



老年代什么时候会进行垃圾回收? 在老年代的空间分配担保机制中可以看到,老年代进行垃圾回收的情况有以下一种情况:
1、每次Young GC之前会进行一些判断,看看是否需要老年代先进行一下Full GC;
2、Young GC之后,存活对象Survivor区放不下,老年代也放不下了,那么触发一次Full GC。


老年代什么时候会发生内存溢出? 在老年代的空间分配担保机制中,我们可以看到,如果是Full GC之后,老年代还是没有足够的空间来存放Young GC之后的对象,那么此时就会发生OOM。


设置参数总结:
-XX:MaxTenuringThreshold:决定了对象多大年龄可以进入老年代
-XX:PretenureSizeThreshold:决定了多大的对象可以进入老年代
-XX:HandlePromotionFailure:是否设置空间分配担保

    推荐阅读