在《深入理解java虚拟机》一书中读到3.6章节,内存分配和回收策略:
预备知识
java堆=年轻代(Eden+Survivor+Survivor)+老年代
Eden:Survivor:Survivor默认比例8:1:1,每次年轻代使用率90%(Eden和一个Survivor)。
一 内存分配策略
1 对象优先在eden分配
可用空间是Eden和一个Survivor,在GC回收发生时,如果存活的对象比另一个survivor空间要大,就直接放到老年代和survivor中,这部分称为分配担保。
2 大对象直接进入老年代
可以通过设置参数,将大对象直接存入老年代,防止了大对象来回复制造成的损失。
3 长期存活的对象将进入老年代
对象在Eden中出生后,并经历过一次GC后仍存活,且被survivor容纳的话,被移动到survivor空间,并记年龄为1岁,对象在每经历一次MinorGC后,年龄都增加1岁,当年龄到达一定程度就会转到老年代,用XX:MaxTenuringThreshold参数进行设置。
下面是对第三节一段代码的理解。
/* 参数设置为
-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
-XX:MaxTenuringThreshold=1
*/
public class TestAllocation {
private static final int _1MB = 1024*1024;
public static void main(String[] args) {
// TODO Auto-generated method stub
testTenuringThreshold();
}
public static void testTenuringThreshold(){
byte[] all1,all2,all3;
all1 = new byte[ _1MB/4];
//什么时候进入老年代取决于XX:MaxTenuringThreshold设置
all2 = new byte[4* _1MB];
all3 = new byte[4* _1MB];
all3 = null;
//all3 = new byte[4* _1MB];
}
}
上述代码运行结果
如果把注释去掉
//all3 = new byte[4* _1MB];
结果是如下
二 结果分析
那么为什么会这样,下面分析内存变化
图1 是执行如下两行后结果
all1 = new byte[ _1MB/4];
all2 = new byte[4* _1MB];
图2
all3 = new byte[4* _1MB];
要给all3分配内存,没空间,进行第一次GC,再在Eden分配空间给all3
图3
all3 = null;
all3 = new byte[4* _1MB];
又要分配新内存4M,没空间,所以进行第二次GC,原来的4M空间的all3引用不再指向它,按理这次GC被回收,图3是进行GC后的图,Eden区因为没有引用指向它被回收,而Survivor2变量因达到优年轻代转向老年代阈值,而被复制到老年代。
可以从GC日志中看出,第二次GC后
[GC[DefNew: 4905K->0K(9216K), 0.0006405 secs],年轻代成为空。
图4
是给all3分配内存的图。所以第二个程序最终结果是图3,而第一个程序最终结果是图2。