《深入理解java虚拟机v3》长期存活的对象将进入老年代 > 代码清单3-9

1. 概述

HotSpot虚拟机中多数收集器都采用了分代收集来管理堆内存,那内存回收时就必须能决策哪些存活对象应当放在新生代,哪些存活对象放在老年代中。

为做到这点,虚拟机给每个对象定义了一个对象年龄(Age)计数器,存储在对象头中(详见第2章)。对象通常在Eden区里诞生,如果经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,该对象会被移动到Survivor空间中,并且将其对象年龄设为1岁。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15),就会被晋升到老年代中。

对象晋升老年代的年龄阈值,可以通过参数-XX: MaxTenuringThreshold设置。

2. 例子

读者可以试试分别以-XX:MaxTenuringThreshold=1-XX:MaxTenuringThreshold=15两种设置来执行代码清单3-9中的testTenuringThreshold()方法,此方法中allocation1对象需要256KB内存,Survivor空间可以容纳。当-XX:MaxTenuringThreshold=1时,allocation1对象在第二次GC发生时进入老年代,新生代已使用的内存在垃圾收集以后非常干净地变成0KB。而当-XX:MaxTenuringThreshold=15时,第二次GC发生后,allocation1对象则还留在新生代Survivor空间,这时候新生代仍然有404KB被占用。

	private static final int _1MB = 1024 * 1024;

	/**
	 * VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
	 * -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1
	 * -XX:+PrintTenuringDistribution -XX:+UseSerialGC
	 */
	public static void testTenuringThreshold() {
    
    
		byte[] allocation1, allocation2, allocation3;
		allocation1 = new byte[_1MB / 4]; // 什么时候进入老年代决定于XX:MaxTenuringThreshold设置
		allocation2 = new byte[4 * _1MB];
		allocation3 = new byte[4 * _1MB];
		allocation3 = null;
		allocation3 = new byte[4 * _1MB];
	}

	public static void main(String[] args) {
    
    
		testTenuringThreshold();
	}

2.1 以-XX:MaxTenuringThreshold=1参数来运行

JDK1.6环境执行

结果:

[`GC` [DefNew
Desired survivor size 524288 bytes, new threshold 1 (`max 1`)
- `age   1`:     413040 bytes,     413040 total
: 4695K->403K(9216K), 0.0078761 secs] 4695K->4499K(19456K), 0.0079214 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[`GC` [DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
: 4499K->0K(9216K), 0.0013783 secs] 8595K->4499K(19456K), 0.0014185 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4259K [0x32360000, 0x32d60000, 0x32d60000)
  eden space 8192K,  52% used [0x32360000, 0x32788fe0, 0x32b60000)           `Eden区4M,存储内存D`
  `from space 1024K,   0% used` [0x32b60000, 0x32b60000, 0x32c60000)      `survivor 存储0,都移动到老年代`
  to   space 1024K,   0% used [0x32	c60000, 0x32c60000, 0x32d60000)
 tenured generation   total 10240K, used 4499K [0x32d60000, 0x33760000, 0x33760000)
   `the space 10240K,  43% used` [0x32d60000, 0x331c4d80, 0x331c4e00, 0x33760000)   `老年代存储(B:4M+ A:256K+148K)`
 compacting perm gen  total 12288K, used 379K [0x33760000, 0x34360000, 0x37760000)
   the space 12288K,   3% used [0x33760000, 0x337bed80, 0x337bee00, 0x34360000)
    ro space 10240K,  51% used [0x37760000, 0x37c925d0, 0x37c92600, 0x38160000)
    rw space 12288K,  55% used [0x38160000, 0x387fd978, 0x387fda00, 0x38d60000)

红色标记,显示经过2次gc后,新生代的Survivor为0%,说明移入到了老年代

详细分析:

  • 执行main函数
    main线程占用148K,进入Eden
  • allocation1 = new byte[_1MB / 4];
    新生代尝试申请256K内存A,成功,进入Eden区
  • allocation2 = new byte[4 * _1MB];
    新生代尝试申请4M内存B,成功,进入Eden区,此时Eden区占用 (4M+256K+148K)
  • allocation3 = new byte[4 * _1MB];
    新生代尝试申请4M内存C,由于Eden区总大小8M,已经占用(4M+256K+148K)发现不够用了,触发GC:
    • A进入Survivor,因为256K<1M ,此时Survivor占用(256K+148K)
    • B进入老年代,因为4M>1M,Survivor放不下,此时老年代(4M)
    • C进入Eden区,此时Eden区占用 (4M)
  • allocation3 = null;
    让Eden区的内存C符合GC条件,可以注释掉,不影响本例的目的
  • allocation3 = new byte[4 * _1MB];
    尝试在新生代Eden区申请4M内存D,发现内存刚好到达8M,几乎满了,触发GC
    • 内存C直接被清除掉,此时Eden区使用0
    • Survivor占用(256K+148K)生命周期+1后>1,符合晋升老年代条件,此时老年代(4M+256K+148K),Survivor占用0
    • 在Eden区申请4M,存储内存D

2.2 以-XX:MaxTenuringThreshold=15参数来运行

多次执行的结果和书上一致

[`GC `[DefNew
Desired survivor size 524288 bytes, new threshold 15 (`max 15`)
- `age   1`:     413040 bytes,     413040 total
: 4695K->403K(9216K), 0.0045132 secs] 4695K->4499K(19456K), 0.0045568 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[`GC` [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- `age   2`:     413040 bytes,     413040 total
: 4499K->403K(9216K), 0.0009964 secs] 8595K->4499K(19456K), 0.0010297 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4827K [0x32360000, 0x32d60000, 0x32d60000)
  eden space 8192K,  54% used [0x32360000, 0x327b1fa8, 0x32b60000)       `eden存储D:4M`
  from space 1024K,  `39% used` [0x32b60000, 0x32bc4d70, 0x32c60000)     `// Survivor存储A:256K+148K`
  to   space 1024K,   0% used [0x32c60000, 0x32c60000, 0x32d60000)
 tenured generation   total 10240K, used 4096K [0x32d60000, 0x33760000, 0x33760000)
   the space 10240K,  `40% used` [0x32d60000, 0x33160010, 0x33160200, 0x33760000)      `//老年代存储B:4M`
 compacting perm gen  total 12288K, used 379K [0x33760000, 0x34360000, 0x37760000)
   the space 12288K,   3% used [0x33760000, 0x337bed80, 0x337bee00, 0x34360000)
    ro space 10240K,  51% used [0x37760000, 0x37c925d0, 0x37c92600, 0x38160000)
    rw space 12288K,  55% used [0x38160000, 0x387fd978, 0x387fda00, 0x38d60000)

注意:重启eclipse执行第一次结果会多一行高亮显示的:

[GC [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
- age   1:     413040 bytes,     413040 total
: 4695K->403K(9216K), 0.0049113 secs] 4695K->4499K(19456K), 0.0049442 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC [DefNew
Desired survivor size 524288 bytes, new threshold 15 (max 15)
`- age   1:        136 bytes,        136 total`   `//多出一行`
- age   2:     412832 bytes,     412968 total
: 4663K->403K(9216K), 0.0010729 secs] 8759K->4499K(19456K), 0.0011123 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4663K [0x32360000, 0x32d60000, 0x32d60000)
  eden space 8192K,  52% used [0x32360000, 0x32788fe0, 0x32b60000)
  from space 1024K,  39% used [0x32b60000, 0x32bc4d28, 0x32c60000)
  to   space 1024K,   0% used [0x32c60000, 0x32c60000, 0x32d60000)
 tenured generation   total 10240K, used 4096K [0x32d60000, 0x33760000, 0x33760000)
   the space 10240K,  40% used [0x32d60000, 0x33160010, 0x33160200, 0x33760000)
 compacting perm gen  total 12288K, used 379K [0x33760000, 0x34360000, 0x37760000)
   the space 12288K,   3% used [0x33760000, 0x337bed80, 0x337bee00, 0x34360000)
    ro space 10240K,  51% used [0x37760000, 0x37c925d0, 0x37c92600, 0x38160000)
    rw space 12288K,  55% used [0x38160000, 0x387fd978, 0x387fda00, 0x38d60000)

猜你喜欢

转载自blog.csdn.net/m0_45406092/article/details/108655593