Parallel Scavenge收集器与PretenureSizeThreshold

本文通过实验展示了Parallel Scavenge垃圾收集器在不同条件下如何处理大对象的内存分配,揭示了大对象直接进入老年代的条件并非仅由eden区剩余空间决定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

的确已有定论: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分配内存时,eden明显已经不够,却没有触发GC,而是将alloc4直接分配到了老年代上。

紧接着是更有趣的,如果更改对象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上
从这两次运行结果来看,可以作出这么一个假设:在Parallel Scavenge + Parallel Old模式下,大对象是否直接进入老年代,并不是简单根据eden是否还有足够的空间来决定的。这里面的详细算法,有待对JVM的源码进行分析后才能揭开其神秘的面纱。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值