在发生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已失效