Serial/Serial Old 和 Parallel Scavenge/Parallel Old 对象内存分配对比

1.前言

    本渣最近在学习《深入理解Java虚拟机 JVM高级特性与最佳实践》,看到关于对象内存分配的时候,书中有段测试对象分配的代码,自信满满地以为已经理解了其内存分配策略,结果自己在电脑上敲了一下,发现并不是那么回事,运行结果根本不一样,这是怎么回事呢?难道是作者大神搞错了?不,一定不是。分析本机GC日志发现,代码示例和本机默认使用的收集器不一样,代码示例中用的是 Serial/Serial Old 组合收集器,而本机的默认的是 Parallel Scavenge/Parallel Old 组合收集器,这才明白内存分配策略要根据具体使用的收集器而定。以下将针对这两种收集器组合进行详细对比。


2.环境:

    java version "1.7.0_45"
    Java(TM) SE Runtime Environment (build 1.7.0_45-b18)

    Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

    JVM 为 Server 模式


3.代码

    以下代码限定了Java堆大小为20M,不可扩展,其中新生代为10M,老年代为10M,且新生代中 Eden:Survivor=8:1。

/**
 * 对象内存分配测试
 * @author huangyan
 */
public class ObjectAllocationTest {

	private static final int _1MB = 1024 * 1024;
	
	/**
	 * 1.VM 参数,默认使用 Parallel Scavenge/Parallel Old:
	 * 		-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8
	 * 2.VM 参数,指定使用 Serial/Serial Old: 
	 * 		-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
	 * VM 参数分析:
	 * 	-verbose:gc			在虚拟机发生内存回收时在输出设备显示信息,
	 * 						格式如下: [Full GC 256K->160K(124096K), 0.0042708 secs] 
	 * 						该参数用来监视虚拟机内存回收的情况。
	 * 	-Xms20M				Java堆大小最小20M
	 * 	-Xmx20M				Java堆大小最大20M
	 * 	-Xmn10M				新生代10M
	 * 	-XX:+PrintGCDetails	打印GC详细信息
	 * 	-XX:SurvivorRatio=8	Dden区:Survivor区=8:1
	 * 
	 * 	-XX:+UseSerialGC	指定使用 Serial/Serial Old 组合收集器
	 */
	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];
	}
	
	public static void main(String[] args) {
		ObjectAllocationTest.testAllocation();
	}
	
}

4. 默认使用 Parallel Scavenge/Parallel Old 组合收集器
    (1) allocation4 大小为 4M

        代码:allocation4 = new byte[4 * _1MB];

        VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

        运行结果:      

Heap
 PSYoungGen      total 9216K, used 6980K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 85% used [0x00000000ff600000,0x00000000ffcd1080,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)
 PSPermGen       total 21504K, used 2505K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c72530,0x00000000faf00000)

        结果分析:

            代码执行到 allocation4 = new byte[4 * _1MB]; 时,前三个对象已经占用 Eden 区 6M,此时,虚拟机发现整个新生代(Eden+Survivor)已经不够分配所需的 4M(新生代共10M,Eden共8M,Eden+Survivor1=9M,9M-6M=3M<4M),因此不会发生 Minor GC,allocation4 直接进入老年代;

            此时,allocation4(4M) 在 老年代,allocation1、allocation2、allocation3 (6M)在新生代 Eden 区,Survivor 区空闲。

    (2) allocation4 大小为 3M

        修改代码 allocation4 = new byte[4 * _1MB]; 为 allocation4 = new byte[3 * _1MB];

        VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

        运行结果:

[GC [PSYoungGen: 6816K->600K(9216K)] 6816K->6744K(19456K), 0.0033671 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 600K->0K(9216K)] [ParOldGen: 6144K->6613K(10240K)] 6744K->6613K(19456K) [PSPermGen: 2498K->2497K(21504K)], 0.0083242 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 9216K, used 3563K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 43% used [0x00000000ff600000,0x00000000ff97af60,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 6613K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 64% used [0x00000000fec00000,0x00000000ff275688,0x00000000ff600000)
 PSPermGen       total 21504K, used 2507K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
  object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c72d28,0x00000000faf00000)

        结果分析:

            代码执行到 allocation4 = new byte[3 * _1MB]; 时,前三个对象已经占用 Eden 区 6M,此时,虚拟机发现 Eden 区所剩内存不够所需3M, 但整个新生代(Eden+Survivor)还够分配,因此会发生一次 Minor GC;
            这时候虚拟机发现已有的三个对象还存活而且太大不能放入 Survivor 区,所以只好启动分配担保机制将这三个对象提前转移到老年代中;

           此时,allocation4(3M) 进入 Eden区,allocation1、allocation2、allocation3(6M) 进入老年代,Survivor 区空闲。


5. 指定使用 Serial/Serial Old 组合收集器
    (1) allocation4 大小为 4M

        代码:allocation4 = new byte[4 * _1MB];

        VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC

        运行结果:

[GC[DefNew: 6816K->469K(9216K), 0.0041440 secs] 6816K->6613K(19456K), 0.0041877 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 5057K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  56% used [0x00000000f9a00000, 0x00000000f9e7af60, 0x00000000fa200000)
  from space 1024K,  45% used [0x00000000fa300000, 0x00000000fa375658, 0x00000000fa400000)
  to   space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 tenured generation   total 10240K, used 6144K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  60% used [0x00000000fa400000, 0x00000000faa00030, 0x00000000faa00200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2508K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb0730b0, 0x00000000fb073200, 0x00000000fc2c0000)
No shared spaces configured.

        结果分析:

            代码执行到 allocation4 = new byte[4 * _1MB]; 时,因为前三个对象已经占用 Eden 区 6M,虚拟机发现 Eden 区已经不够分配 allocation4 所需的 4M,因此会发生一次 Minor GC;
            这时候虚拟机发现已有的三个对象(共6M)还存活而且太大不能放入 Survivor 区(共1M),所以只好启动分配担保机制将这三个对象提前转移到老年代中;
            这次 GC 完成后,allocation4 进入 Eden 区(占用4M),allocation1、allocation2、allocation3 进入老年代(占用6M)。

    (2) allocation4 大小为 3M

        修改代码 allocation4 = new byte[4 * _1MB]; 为 allocation4 = new byte[3 * _1MB];

        VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC

        运行结果:

[GC[DefNew: 6816K->469K(9216K), 0.0049430 secs] 6816K->6613K(19456K), 0.0049835 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4033K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
  eden space 8192K,  43% used [0x00000000f9a00000, 0x00000000f9d7af60, 0x00000000fa200000)
  from space 1024K,  45% used [0x00000000fa300000, 0x00000000fa375658, 0x00000000fa400000)
  to   space 1024K,   0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
 tenured generation   total 10240K, used 6144K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
   the space 10240K,  60% used [0x00000000fa400000, 0x00000000faa00030, 0x00000000faa00200, 0x00000000fae00000)
 compacting perm gen  total 21248K, used 2508K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
   the space 21248K,  11% used [0x00000000fae00000, 0x00000000fb0730b0, 0x00000000fb073200, 0x00000000fc2c0000)
No shared spaces configured.

        结果分析:

            运行结果同4M时类似,因为需要分配 allocation4 时,其所需的3M空间同样大于了Eden所剩的2M空间,因此还是会发生一次 Minor GC 。

            这次 GC 完成后,allocation4 进入 Eden 区(占用3M),allocation1、allocation2、allocation3 进入老年代(占用6M)。       


6. 总结

    对于 Serial/Serial Old,是否发生 Minor GC 的判定条件是:Eden 区所剩内存是否足够分配该对象,若不够,即 Minor GC。

    对于 Parallel Scavenge/Parallel Old,是否发生 Minor GC 的判定条件是:Eden 区和整个新生代所剩内存是否足够分配该对象,若够但 Eden 不够,则发生一次 Minor GC;若不够,则不会 Minor GC,该对象直接进入老年代。


    最后,若不是动手实践了一番,我一定不会很快意识到不同收集器有不同的内存分配策略这一事实。所以,实践才是检验真理的唯一标准。以后的学习中一定要多动手多动手多动手。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值