12–内存分配策略–动态对象年龄判定
1、动态对象年龄判定
如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。
2、测试
2.1、代码
public class Test {
private static final int _1MB = 1024 * 1024;
//参数
//-verbose:gc
//-Xms20M
//-Xmx20M
//-Xmn10M
//-XX:+PrintGCDetails
//-XX:SurvivorRatio=8
//-XX:MaxTenuringThreshold=15
//-XX:+PrintTenuringDistribution
//-XX:+UseSerialGC
public static void main(String[] args) {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[_1MB / 4];
allocation2 = new byte[_1MB / 4];
// allocation1+allocation2大于survivor空间的一半 ,进入老年代
allocation3 = new byte[4 * _1MB];
allocation4 = new byte[4 * _1MB];
allocation4 = null;
allocation4 = new byte[4 * _1MB];
}
}
2.1、日志
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age 1: 1003080 bytes, 1003080 total
: 5279K->979K(9216K), 0.0055052 secs] 5279K->5075K(19456K), 0.0055649 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 1: 256 bytes, 256 total
: 5403K->0K(9216K), 0.0024450 secs] 9499K->5075K(19456K), 0.0024776 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 4260K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e28fd0, 0x00000000fa200000)
from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200100, 0x00000000fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
tenured generation total 10240K, used 5075K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
the space 10240K, 49% used [0x00000000fa400000, 0x00000000fa8f4c10, 0x00000000fa8f4e00, 0x00000000fae00000)
compacting perm gen total 21248K, used 2540K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 11% used [0x00000000fae00000, 0x00000000fb07b358, 0x00000000fb07b400, 0x00000000fc2c0000)
No shared spaces configured.
3、分析
即使MaxTenuringThreshold=15,会发现运行结果中Survivor的空间占用仍然为0%,而老年代比预期增加了9%,也就是说allocation1、allocation2对象都直接进入了老年代,而没有等到15岁的临界年龄。因为这两个对象加起来已经达到了0.5M,并且它们是同年的,满足同年对象达到Survivor空间的一半规则。我们只要注释掉其中一个对象的new操作,就会发现另外一个不会晋升到老年代中去。
3、分析
3.1、第1次GC
allocation1对象和allocation2对象以及allocation3对象都可以存放进Eden区,当allocation4对象申请分配的时候空间不足,这时进行第一次GC回收。
3.1.1、日志解读
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age 1: 1003080 bytes, 1003080 total
: 5279K->979K(9216K), 0.0055052 secs] 5279K->5075K(19456K), 0.0055649 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
GC :
代表发生了一次垃圾回收,前面没有Full修饰,表明这时一次Minor GC;
new threshold 1 (max 15)
当前对象的年龄为1,最大年龄为15
5279K->979K(9216K)
三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。
5279K->5075K(19456K)
三个参数分别为:堆区垃圾回收前的大小,堆区垃圾回收后的大小,堆区总大小。
0.0055649 secs
代表本次新生代GC耗时
年轻代回收空间
5279K-979K=4300K=4.1992M
堆回收空间
5279K-5075K=204K
进入老年代空间
4300K-204K=4096=4M
GC前其他对象空间
5279K-4*1024- (1024/4)*2=671K
GC后其他对象空间
979K-512K=464K
3.1.2、 内存变化图
allocation1对象和allocation2对象进入s1区,大对象allocation3直接进入老年代
3.1、第2次GC
allocation4 = null;
allocation4 = new byte[4 * _1MB];
当执行上面代码时候,触发第二次GC,但是由于a1+a2这两个对象加起来已经到达了512KB,并且它们是同年龄的,满足同年对象达到Survivor空间一半的规则。根据动态年龄判断规则,这时直接进入老年代
3.1.1、日志解读
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age 1: 256 bytes, 256 total
: 5403K->0K(9216K), 0.0024450 secs] 9499K->5075K(19456K), 0.0024776 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
GC :
代表发生了一次垃圾回收,前面没有Full修饰,表明这时一次Minor GC;
new threshold 1 (max 15)
当前对象的年龄为1,最大年龄为15
5403K->0K(9216K)
三个参数分别为:GC前该内存区域(这里是年轻代)使用容量,GC后该内存区域使用容量,该内存区域总容量。
9499K->5075K(19456K)
三个参数分别为:堆区垃圾回收前的大小,堆区垃圾回收后的大小,堆区总大小。
0.0024450 secs
代表本次新生代GC耗时
年轻代回收空间
5403K-0K=5403K=5.276M
堆回收空间
9499K-5075K=4424K
进入老年代空间
5403K-4424K=979K
其中464K是第1次GC中留下的其他对象。也就是说除此之外还有979K-464K=512K进入老年代
也就是a1+a2的值