《深入理解java虚拟机》第3版
代码清单3-7 新生代Minor GC
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
}
使用JDK1.8做验证,发现和书中描述不一致。
考虑前面版本成书较早,可能基于较早的jre版本运行。
首先考虑的原因是默认的垃圾收集器不同,JDK8默认为Parallel Scavenge+Parall Old组合 跟作者的JDK6使用的Serial+Serial Old组合的算法不一致。
因此增加参数:-XX:+UseSerialGC 后再次测试,还是相同结果。
然后想到新生代对象晋升老年代的规则之一:大对象直接分配到老年代。
因此增加参数:-XX:PretenureSizeThreshold=10485760 后测试,还是没有达到预期。
后来想到,老年代的4M不一定就是一个对象的4M,也可能是2个2M的对象。于是想到通过打断点运行,并配合可视化工具 visualvm 来动态观察JVM堆内存各区的变化情况。
通过分析知道,JVM初始化时有部分对象影响了测试结果。即初始化时对象大小超过2M,到运行allocation1、allocation2后,在allocation3对象分配时Eden区已经不够存放,因此在预期之前发生了MinorGC。因此在运行目标代码前增加一行
System.gc();
解决了困惑。
可能有人会问,原来那个正好40%,即4M,而我这边断点之后其实不是4M,而是6M+?这个猜测是因为断点导致一些对象被引用着而存活下来了,而不打断点运行时直接被当垃圾回收了。
结束。