对象的内存分配,往大方向讲,就是在堆上分配。对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。 少数情况下也可能会直接分配在老年代中,分配的规则并不是百分之百固定的,其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。接下来介绍几条最普遍的内存分配规则,并通过代码去验证这些规则。
private static final int_1MB=1024*1024; /** *VM参数:-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails -XX:SurvivorRatio=8 */ public static void testAllocation(){ byte[]allocation1,allocation2,allocation3,allocation4; allocation1=new byte[2*_1MB]; allocation2=new byte[2*_1MB]; allocation3=new byte[2*_1MB]; allocation4=new byte[4*_1MB];//出现一次Minor GC }上述代码的testAllocation()方法中,尝试分配3个2MB大小和1个4MB大小的对象,在运行时通过-Xms20M、 -Xmx20M、 -Xmn10M这3个参数限制了Java堆大小为20MB,不可扩展,其中10MB分配给新生代,剩下的10MB分配给老年代。 -XX:SurvivorRatio=8决定了新生代中Eden区与一个Survivor区的空间比例是8:1,从而得出eden space 8192K、 from space 1024K、 to space 1024K的分配结果,新生代总可用空间为9216KB(Eden区+1个Survivor区的总容量)。
[GC[DefNew:6651K->148K(9216K),0.0070106 secs]6651K->6292K(19456K), 0.0070426 secs][Times:user=0.00 sys=0.00,real=0.00 secs] Heap def new generation total 9216K,used 4326K[0x029d0000,0x033d0000,0x033d0000) eden space 8192K,51%used[0x029d0000,0x02de4828,0x031d0000) from space 1024K,14%used[0x032d0000,0x032f5370,0x033d0000) to space 1024K,0%used[0x031d0000,0x031d0000,0x032d0000) tenured generation total 10240K,used 6144K[0x033d0000,0x03dd0000,0x03dd0000) the space 10240K,60%used[0x033d0000,0x039d0030,0x039d0200,0x03dd0000) compacting perm gen total 12288K,used 2114K[0x03dd0000,0x049d0000,0x07dd0000) the space 12288K,17%used[0x03dd0000,0x03fe0998,0x03fe0a00,0x049d0000) No shared spaces configured.结果分析:
private static final int_1MB=1024*1024; /** *VM参数:-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:SurvivorRatio=8 *-XX:PretenureSizeThreshold=3145728 */ public static void testPretenureSizeThreshold(){ byte[] allocation; allocation=new byte[4*_1MB];//直接分配在老年代中 }运行结果:
Heap def new generation total 9216K,used 671K[0x029d0000,0x033d0000,0x033d0000) eden space 8192K,8%used[0x029d0000,0x02a77e98,0x031d0000) from space 1024K,0%used[0x031d0000,0x031d0000,0x032d0000) to space 1024K,0%used[0x032d0000,0x032d0000,0x033d0000) tenured generation total 10240K,used 4096K[0x033d0000,0x03dd0000,0x03dd0000) the space 10240K,40%used[0x033d0000,0x037d0010,0x037d0200,0x03dd0000) compacting perm gen total 12288K,used 2107K[0x03dd0000,0x049d0000,0x07dd0000) the space 12288K,17%used[0x03dd0000,0x03fdefd0,0x03fdf000,0x049d0000) No shared spaces configured.结果分析:
private static final int_1MB=1024*1024; /** *VM参数:-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:SurvivorRatio=8-XX:MaxTenuringThreshold=1 *-XX:+PrintTenuringDistribution */ @SuppressWarnings("unused") public static void testTenuringThreshold(){ byte[] allocation1,allocation2,allocation3; allocation1=new byte[_1MB/4]; //什么时候进入老年代取决于XX:MaxTenuringThreshold设置 allocation2=new byte[4*_1MB]; allocation3=new byte[4*_1MB]; allocation3=null; allocation3=new byte[4*_1MB]; }结果如下:
[GC[DefNew Desired Survivor size 524288 bytes,new threshold 1(max 1) -age 1:414664 bytes,414664 total :4859K->404K(9216K),0.0065012 secs]4859K->4500K(19456K),0.0065283 secs][Times:user=0.02 sys=0.00,real=0.02 secs] [GC[DefNew Desired Survivor size 524288 bytes,new threshold 1(max 1) :4500K->0K(9216K),0.0009253 secs]8596K->4500K(19456K),0.0009458 secs][Times:user=0.00 sys=0.00,real=0.00 secs] Heap def new generation total 9216K,used 4178K[0x029d0000,0x033d0000,0x033d0000) eden space 8192K,51%used[0x029d0000,0x02de4828,0x031d0000) from space 1024K,0%used[0x031d0000,0x031d0000,0x032d0000) to space 1024K,0%used[0x032d0000,0x032d0000,0x033d0000) tenured generation total 10240K,used 4500K[0x033d0000,0x03dd0000,0x03dd0000) the space 10240K,43%used[0x033d0000,0x03835348,0x03835400,0x03dd0000) compacting perm gen total 12288K,used 2114K[0x03dd0000,0x049d0000,0x07dd0000) the space 12288K,17%used[0x03dd0000,0x03fe0998,0x03fe0a00,0x049d0000) No shared spaces configured.以MaxTenuringThreshold=15参数来运行的结果:
[GC[DefNew Desired Survivor size 524288 bytes,new threshold 15(max 15) -age 1:414664 bytes,414664 total :4859K->404K(9216K),0.0049637 secs]4859K->4500K(19456K),0.0049932 secs][Times:user=0.00 sys=0.00,real=0.00 secs] [GC[DefNew Desired Survivor size 524288 bytes,new threshold 15(max 15) -age 2:414520 bytes,414520 total :4500K->404K(9216K),0.0008091 secs]8596K->4500K(19456K),0.0008305 secs][Times:user=0.00 sys=0.00,real=0.00 secs] Heap def new generation total 9216K,used 4582K[0x029d0000,0x033d0000,0x033d0000) eden space 8192K,51%used[0x029d0000,0x02de4828,0x031d0000) from space 1024K,39%used[0x031d0000,0x03235338,0x032d0000) to space 1024K,0%used[0x032d0000,0x032d0000,0x033d0000) tenured generation total 10240K,used 4096K[0x033d0000,0x03dd0000,0x03dd0000) the space 10240K,40%used[0x033d0000,0x037d0010,0x037d0200,0x03dd0000) compacting perm gen total 12288K,used 2114K[0x03dd0000,0x049d0000,0x07dd0000) the space 12288K,17%used[0x03dd0000,0x03fe0998,0x03fe0a00,0x049d0000) No shared spaces configured.结果分析:
private static final int_1MB=1024*1024; /** *VM参数:-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:SurvivorRatio=8-XX:MaxTenuringThreshold=15 *-XX:+PrintTenuringDistribution */ @SuppressWarnings("unused") public static void testTenuringThreshold2(){ byte[] allocation1,allocation2,allocation3,allocation4; allocation1=new byte[_1MB/4]; //allocation1+allocation2大于survivo空间一半 allocation2=new byte[_1MB/4]; allocation3=new byte[4*_1MB]; allocation4=new byte[4*_1MB]; allocation4=null; allocation4=new byte[4*_1MB]; }结果如下:
[GC[DefNew Desired Survivor size 524288 bytes,new threshold 1(max 15) -age 1:676824 bytes,676824 total :5115K->660K(9216K),0.0050136 secs]5115K->4756K(19456K),0.0050443 secs][Times:user=0.00 sys=0.01,real=0.01 secs] [GC[DefNew Desired Survivor size 524288 bytes,new threshold 15(max 15) :4756K->0K(9216K),0.0010571 secs]8852K->4756K(19456K),0.0011009 secs][Times:user=0.00 sys=0.00,real=0.00 secs] Heap def new generation total 9216K,used 4178K[0x029d0000,0x033d0000,0x033d0000) eden space 8192K,51%used[0x029d0000,0x02de4828,0x031d0000) from space 1024K,0%used[0x031d0000,0x031d0000,0x032d0000) to space 1024K,0%used[0x032d0000,0x032d0000,0x033d0000) tenured generation total 10240K,used 4756K[0x033d0000,0x03dd0000,0x03dd0000) the space 10240K,46%used[0x033d0000,0x038753e8,0x03875400,0x03dd0000) compacting perm gen total 12288K,used 2114K[0x03dd0000,0x049d0000,0x07dd0000) the space 12288K,17%used[0x03dd0000,0x03fe09a0,0x03fe0a00,0x049d0000) No shared spaces configured.
执行代码中的testTenuringThreshold2()方法,并设置-XX:MaxTenuringThreshold=15,会发现运行结果中Survivor的空间占用仍然为0%,而老年代比预期增加了6%,也就是说,allocation1、 allocation2对象都直接进入了老年代,而没有等到15岁的临界年龄。 因为这两个对象加起来已经到达了512KB,并且它们是同年的,满足同年对象达到Survivor空间的一半规则。 我们只要注释掉其中一个对象new操作,就会发现另外一个就不会晋升到老年代中去了。