【JVM】12_空间分配担保

本文探讨了在Java虚拟机中MinorGC触发机制及HandlePromotionFailure的作用。当新生代对象无法完全移至Survivor区时,HandlePromotionFailure决定进行冒险的MinorGC还是直接进行Full GC。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。


新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在MinorGC后仍然存活的情况(最极端的情况就是内存回收后新生代中所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。与生活中的贷款担保类似,老年代要进行这样的担保,前提是老年代本身还有容纳这些对象的剩余空间,一共有多少对象会活下来在实际完成内存回收之前是无法明确知道的,所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值,与老年代的剩余空间进行比较,决定是否进行Full GC来让老年代腾出更多空间。


取平均值进行比较其实仍然是一种动态概率的手段,也就是说,如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure)。如果出现了HandlePromotionFailure失败,那就只好在失败后重新发起一次Full GC。虽然担保失败时绕的圈子是最大的,但大部分情况下都还是会将HandlePromotionFailure开关打开,避免Full GC过于频繁。


/**
 * VM参数
 * -verbose:gc
 * -Xms20M
 * -Xmx20M
 * -Xmn10M
 * -XX:+UseSerialGC
 * -XX:SurvivorRatio=8
 * -XX:+PrintGCDetails
 * -XX:+HandlePromotionFailure          //此参数在JDK1.6.0_24之后的版本不再起作用,默认就是+HandlePromotionFailure
 */
public class Main {

    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) {

        byte[] b1,b2,b3,b4,b5,b6,b7;
        b1 = new byte[2 * _1MB];
        b2 = new byte[2 * _1MB];
        b3 = new byte[2 * _1MB];
        b1 = null;

        //这里进行第一次GC,把b1第一次引用的对象GC掉,同时把b2,b3引用的对象放入老年代,当前老年代占用了4M
        b4 = new byte[2 * _1MB];

        b5 = new byte[2 * _1MB];
        b6 = new byte[2 * _1MB];
        b4 = null;
        b5 = null;
        b6 = null;
        /**
         * 第二次GC
         * Survivor的空间不足存放b7引用的对象, 接下来执行的GC政策由HandlePromotionFailure来决定
         *
         * HandlePromotionFailure如果true(JDK16.0.24以后就可以当作一直是true,因为这个参数被无视了):
         * JVM会冒险进行一次Minor GC来使b7对象得到空间存放在新生代,如果GC后发现空间还是不足,则会进行一次Major GC(Full GC)
         *
         * HandlePromotionFailure如果false
         * JVM会直接进行Major GC(Full GC)
         */
        b7 = new byte[2*_1MB];
    }
}

运行结果:

[GC [DefNew: 6987K->189K(9216K), 0.0033408 secs] 6987K->4285K(19456K), 0.0033769 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [DefNew: 6834K->189K(9216K), 0.0004289 secs] 10930K->4285K(19456K), 0.0004515 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 2374K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  26% used [0x00000000f9a00000, 0x00000000f9c22520, 0x00000000fa200000)
  from space 1024K,  18% used [0x00000000fa200000, 0x00000000fa22f410, 0x00000000fa300000)
  to   space 1024K,   0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
 tenured generation   total 10240K, used 4096K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  40% used [0x00000000fa400000, 0x00000000fa800020, 0x00000000fa800200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 3450K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  16% used [0x00000000fae00000, 0x00000000fb15f038, 0x00000000fb15f200, 0x00000000fc2c0000)
No shared spaces configured.

注意:JDK1.6024之后HandlePromotionFailure已失效




### Java 新生内存分配担保机制概念及工作原理 #### 概念 Java 虚拟机中的新生内存分配担保机制是一种用于处理特殊情况下的内存分配策略。当新生的 Eden 区域不足以容纳新创建的对象时,会触发 **Minor GC**。然而,在 Minor GC 的过程中,可能会遇到一种情况:Survivor 区域的空间不足,无法存放从 Eden 和另一个 Survivor 区迁移过来的对象。此时,JVM 将启动分配担保机制,将这些对象直接提升至老年[^2]。 #### 工作原理 1. **Eden 区满触发 Minor GC**: 当 Eden 区没有足够的连续空间分配新的对象时,JVM 会发起一次 Minor GC。 2. **对象复制过程**: 在 Minor GC 中,存活的对象会被复制到其中一个 Survivor 区(假设为 S0),而另一个 Survivor 区(S1)则被清空并准备接收下一轮的存活对象。 3. **Survivor 不足的情况**: 如果在 Minor GC 结束后,仍有部分对象无法放入 Survivor 区,则这些对象会被直接移动到老年。这一行为就是所谓的“分配担保”。 4. **条件判断**: 是否启用分配担保取决于当前老年是否有足够的剩余空间来接纳这些溢出的对象。如果老年有足够的空间,则可以顺利完成操作;否则可能引发 Full GC 或抛出 `OutOfMemoryError` 错误。 以下是实现该逻辑的一个简化伪码示例: ```java if (edanSpace.isFull()) { triggerMinorGC(); } // 判断是否需要进行分配担保 if (!survivorSpace.canHold(liveObjects)) { promoteToOldGeneration(liveObjects); } else { copyToSurvivor(liveObjects); } ``` #### 特殊场景分析 - **极端情况下全量垃圾回收**: 若老年也缺乏可用空间以支持上述担保动作,则不得不执行 Full GC 来清理整个堆区,从而腾挪更多资源供程序继续运行。 - **元空间的影响**: 自 JDK 1.8 开始,永久被移除并替换成了元空间(Meta Space),这减少了因类加载过多而导致 OutOfMemoryError 风险的可能性[^3]。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值