空间担保机制

219 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

在上文介绍了JVM的垃圾回收机制,了解了垃圾回收的几种算法,以及垃圾回收的过程与作用,本文介绍垃圾回收下会发生的问题以及解决方案。

jvm一次完整的gc过程

先来复习下jvm一次完整的gc过程,首先jvm堆内存划分为两个部分,新生代和老年代。

新生代有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1。

新生代的垃圾回收(又称Minor GC)后只有少量对象存活,所以选用复制算法,只需要少量的复制成本就可以完成回收。

  • 对象优先在Eden分配。当 eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC。

    • 在 Eden 区执行了第一次 GC 之后,存活的对象会被移动到其中一个 Survivor 分区;
    • Eden 区再次 GC,这时会采用复制算法,将 Eden 和 from 区一起清理,存活的对象会被复制到 to 区;
    • 移动一次,对象年龄加 1,对象年龄大于一定阀值会直接移动到老年代。GC年龄的阀值可以通过参数 -XX:MaxTenuringThreshold 设置,默认为 15;
    • 动态对象年龄判定:Survivor 区相同年龄所有对象大小的总和 > (Survivor 区内存大小 * 这个目标使用率)时,大于或等于该年龄的对象直接进入老年代。其中这个使用率通过 -XX:TargetSurvivorRatio 指定,默认为 50%;
    • Survivor 区内存不足会发生担保分配,超过指定大小的对象可以直接进入老年代。
  • 大对象直接进入老年代,大对象就是需要大量连续内存空间的对象(比如:字符串、数组),为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。

  • 老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和老年代

可以注意到的是当新生代进行gc时,由于相同年龄所有对象大小的总和 > (Survivor 区内存大小 * 这个目标使用率)时,大于或等于该年龄的对象直接进入老年代。那么如果此时老年代空间也不足了怎么办?这时候其实是会触发一个叫做空间担保机制的东西。

空间担保机制

空间担保机制描述的是,在yougGc时新生代中有大量对象存活,而survivor区放不下了,这时候必须移动到老年代中,但是这时老年代的空间也不足,放不下这些对象,那该怎么办呢?这时会有一个空间担保机制来确保这些对象能够进入老年代

空间担保机制首先要配置 -XX:HandlePromotionFailure 如果允许,就会判断老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试进行一次YoungGC,尽快这次YoungGC是有风险的。如果小于,或者 -XX:HandlePromotionFailure 参数不允许担保失败,这时就会进行一次 Full GC。

在允许担保失败并尝试进行YoungGC后,可能会出现三种情况:

① YoungGC后,存活对象小于survivor大小,此时存活对象进入survivor区中
② YoungGC后,存活对象大于survivor大小,但是小于老年大可用空间大小,此时直接进入老年代。
③ YoungGC后,存活对象大于survivor大小,也大于老年代可用空间大小,老年代也放不下这些对象了,此时就会发生“Handle Promotion Failure”,就触发了 Full GC。如果 Full GC后,老年代还是没有足够的空间,此时就会发生OOM内存溢出了。

image.png