深入jvm之对象如何进入老年代

简介

我们知道,整个jvm堆分为新生代和老年代,新生代的对象在进行垃圾回收的时候,可能会进入到老年代,那么我们知道年轻代的对象是如何进入到老年代的吗?

年轻代空间分配

新生代分成Eden,Survivor To,Survivor From三个区域,默认空间比例为8:1:1,可以通过 -XX:SurvivorRadio 参数配置,来修改比例大小,比如 -XX:SurvivorRadio配置成2,则表示年轻代空间比例为 2:1:1。下面以1GB的年轻代空间为例,如图所示:

深入jvm之对象如何进入老年代
当有新的对象分配的时候,首先分配到Eden区域,当Eden空间不足时,触发MinorGc,把Eden区域的还存活的对象放到Survivor To区域,其他全部回收掉,当再次触发MinorGc时,会把Eden区和Survivor To区的存活对象放到Survivor From区,其他回收掉,如此循环。
在进行MinorGc的过程中,会出现各种情况,使得年轻代的对象进入到老年代里,下面我们看看进入到老年代有哪些情况。

对象年龄

每个对象都有对象头,存放对象回收年龄,每经历过一次MinorGc后,对象没被回收,则对象年龄+1,
当某个对象连续回收15次之后,仍然存活,那么直接进入老年代空间。

深入jvm之对象如何进入老年代

在默认配置下是年龄到15次的时候会进入到老年代,也可以通过参数配置。

通过 XX:MaxTenuringThreshold进行配置,最大只能配置到15,配置16及以上会报错。

想一想,为什么最大只能配置到15呢?

动态对象年龄判断

除了上面的静态对象年龄判断,还有一种情况也会判断对象年龄。
Survivor区域中,如果存活对象的大小超过当前区域大小的50%,那么按照对象年龄排序,大于等于这批对象最小年龄的对象都会进入到老年代空间。
如图所示:
深入jvm之对象如何进入老年代
假设Survivor To区域已有存活对象A、B、C,其中A+B两个对象的大小加起来超过了Survivor To区域的一半,那么会以A和B对象的最小对象年龄为基线,大于等于这个基线的对象直接回收到老年代。

大对象直接进入老年代

我们知道新生代的垃圾回收算法采用了复制算法,复制算法有一个缺点就是当存在大量大对象的时候会导致每次回收效率下降,因为要复制移动对象
新生代为了避免复制算法的这个缺点,采用了大对象直接进入老年代的策略,防止大对象长时间在新生代存活,影响gc效率。
通过 -XX:PretenureSizeThreshold 参数配置,单位大小是字节,比如 -XX:PretenureSizeThreshold =1024,那么就表示超过1kb大小的对象在垃圾回收时直接进入到老年代,如图所示:

深入jvm之对象如何进入老年代
当年轻代开始回收的时候,发现某个对象大小超过了XX:PretenureSizeThreshold 配置的时候,该对象直接进入到老年代空间中。

Survivor区域对象空间不足

每次MinorGc的时候,都会把年轻代里其中两个区域还存活的对象放在另外一个空余的区域里,也就是说两个区域的空间是大于另外一个区域的。
如果存在一种极端情况,某次回收,两个区域中还存活的对象空间超过了另外一个区域,这时候另外一个区域的空间是不是就是不够存放这些存活对象了,那这时候应该怎么办?

这时候就需要把这些对象直接放到老年代里,如图所示:

深入jvm之对象如何进入老年代
Eden区域和SurvivorTo区域在Gc回收之后还存活的对象是120MB,这个时候,Survivor From空间肯定不够存放Eden区域的存活对象了

那么根据我们上面说的,Eden和SurvivorTo区域的存活对象就不会进入到Survivor From区域中,而是直接进入到老年代里面。

老年代空间分配担保机制

在上一个规则中,我们知道新生代Survivor存放的空间不足时,会转移到老年代里,如果老年代空间也不足了呢?

这就引出了我们现在要说的这个规则,老年代空间分配担保机制。
jvm在Minor Gc之前,会检查老年代可用空间,是否大于新生代所有对象的大小,如果空间满足,那么自然不用说,按照前面的规则开始执行。
如果空间不满足,这个时候如果出现一种极端情形,也就是年轻代的对象在gc之后,全部都还存活,那么老年代的空间也不够存放了,这时候要怎么办呢?

这个时候要看一个参数 -XX:HandlePromotionFailure 是否设置了,如果设置了就会进行新的检查:现在老年代的空间是否满足于之前每次Minor Gc后进入老年代对象的平均大小,如果满足了,说明这次Minor Gc有可能也是空间足够的,所以,会直接进行Minor Gc;如果不满足或者 -XX:HandlePromotionFailure 参数未配置,那么会直接进行Full Gc,如图所示:

深入jvm之对象如何进入老年代

在进行空间分配担保之后Minor Gc,会出现几种可能:

  1. Minor Gc之后,存活对象小于Survivor 区域,那么就会直接进入到Survivor区域。
  2. Minor Gc之后, 存活对象大于Survivor 区域,小于老年代空间,直接进入到老年代空间。
  3. Minor Gc之后,存活对象还是大于老年代空间,那么还是会进行Full Gc,如果Full Gc后,空间还是不够,则抛出 OutMemoryException异常。
上一篇:12 - 垃圾回收(下)


下一篇:JVM垃圾回收机制