的确已有定论:Paralle Scavenge收集器不认识PretenureSizeThreshold参数。但我在准备Java垃圾收集器和内存分配策略课件的过程中,发现了一个有趣的事。先记录下来,后续找到解答了再补充此文。
本人开发环境的JVM版本信息如下:
>java -version
java version "1.8.0_101"
Java(TM) SE Runtime Environment (build 1.8.0_101-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode)
测试代码如下:
private static final int _1MB = 1024 * 1024;
/*
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
*/
public static void main(String[] args) throws Exception {
byte[] alloc1, alloc2, alloc3, alloc4;
alloc1 = new byte[2 * _1MB];
System.out.println("alloc1 " + printAddressOf(alloc1));
alloc2 = new byte[2 * _1MB];
System.out.println("alloc2 " + printAddressOf(alloc2));
alloc3 = new byte[2 * _1MB];
System.out.println("alloc3 " + printAddressOf(alloc3));
alloc4 = new byte[4 * _1MB];
System.out.println("alloc4 " + printAddressOf(alloc4));
}
printAddressOf是一个简单的获取对象在内存的地址的方法。
用VM参数-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8运行上述代码,在控制台得到的输出如下:
alloc1 0x00000000ff71ef88
alloc2 0x00000000ff970e48
alloc3 0x00000000ffb70e58
alloc4 0x00000000fec00000
Heap
PSYoungGen total 9216K, used 7783K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 95% used [0x00000000ff600000,0x00000000ffd99f38,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
Metaspace used 3378K, capacity 4566K, committed 4864K, reserved 1056768K
class space used 388K, capacity 390K, committed 512K, reserved 1048576K
从上面的内容可以看出:
- 我的JVM默认使用的垃圾收集器是Parallel Scavenge + Parallel Old
- 对象alloc1, alloc2, alloc3都被分配到了eden上,而对象alloc4被直接分配到了老年代上
- 没有发生GC
紧接着是更有趣的,如果更改对象alloc4的大小为3MB,用同样的VM参数运行,在控制台得到的输出如下(为了便于比较,我把GC前后的对象内存地址打印了出来):
alloc1 0x00000000ff72d1d0
alloc2 0x00000000ff97f438
alloc3 0x00000000ffb7f448
[GC (Allocation Failure) [PSYoungGen: 7677K->928K(9216K)] 7677K->7080K(19456K), 0.0046181 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 928K->0K(9216K)] [ParOldGen: 6152K->6954K(10240K)] 7080K->6954K(19456K), [Metaspace: 3373K->3373K(1056768K)], 0.0070911 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
alloc4 0x00000000ff600000
alloc1 0x00000000fec00000
alloc2 0x00000000fee005d0
alloc3 0x00000000ff0005e0
alloc4 0x00000000ff600000
Heap
PSYoungGen total 9216K, used 3314K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 40% used [0x00000000ff600000,0x00000000ff93cab8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 6954K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 67% used [0x00000000fec00000,0x00000000ff2caa98,0x00000000ff600000)
Metaspace used 3379K, capacity 4566K, committed 4864K, reserved 1056768K
class space used 388K, capacity 390K, committed 512K, reserved 1048576K
从上面的内容,可以看出:
- 对象alloc1, alloc2, alloc3先是被分配到了eden上
- 在为alloc4分配内存时,似乎也是要分配到eden上,但是eden空间不足,发生了Allocation Failure,进而引发了GC,对eden区进行了回收,将alloc1, alloc2, alloc3移动到了老年代
- 紧接着又发生了Ergonomics,引发了Full GC,对eden和老年代都做了一次回收
- eden上的对象都被移动到了老年代上了,有充足的空间,对象alloc4被分配到了eden上