实战:内存分配与回收策略

1对象优先在Eden分配

大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC

参数:

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

表示Java堆20MB,10MB新生代,10MB老年代,新生代中一个Eden与一个Survivor大小比8:1

代码:

public class JvmTest {
	
	private static final int _1MB = 1024*1024;
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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];  //出现一次MinorGC
	}
	

}

在分配allocation4时,Eden已经被占用6MB,剩余不够4MB,因此发生minor gc,并且三个2MB大小的对象全部无法放入1MB大小的Survior区,所以只能通过分配担保机制提前转移到老年代去。

结果

Heap
 PSYoungGen      total 9216K, used 7291K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 89% used [0x00000000ff600000,0x00000000ffd1efe8,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 2624K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 279K, capacity 386K, committed 512K, reserved 1048576K

这里与书中不同,我的垃圾收集器只将两个2MB对象放入OldGen

2 大对象直接进入老年代

在Java虚拟机中要避免大对象的原因是,在分配空间时,他容易导致内存明明还有不少空间时就提前触发垃圾收集。而当复制对象时,大对象需要高额的复制开销。

参数

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728

表示大于3MB的对象会被直接放入老年代(不过只对Serial和ParNew有效)

代码

public class JvmTest {
	
	private static final int _1MB = 1024*1024;
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		byte[] allocation4 = new byte[4* _1MB];  //出现一次MinorGC
	}
	

}

3长期存活的对象将进入老年代

参数

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+PrintTenuringDistribution

表示年龄到达1变为老年代

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution

表示年龄到达15变为老年代

代码

public class JvmTest {
	
	private static final int _1MB = 1024*1024;
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		byte[] allocation1,allocation2,allocation3;  //出现一次MinorGC
		allocation1 = new byte[_1MB / 4];
		allocation2 = new byte[_1MB * 4];
		allocation3 = new byte[_1MB * 4];
		allocation3 = null;
		allocation3 = new byte[_1MB * 4];
	}
	

}

不过我的结果两种一致,估计是因为参数不适用于我当前的收集器

4动态对象年龄判定

参数与上面的值为15的一致,allocation1和2加起来是512kb,大于survivor的一半,被视为老年代。

public class JvmTest {
	
	private static final int _1MB = 1024*1024;
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		byte[] allocation1,allocation2,allocation3,allocation4;  //出现一次MinorGC
		allocation1 = new byte[_1MB / 4];
		allocation2 = new byte[_1MB / 4];
		allocation3 = new byte[_1MB * 4];
		allocation4 = new byte[_1MB * 4];
		allocation4 = null;
		allocation4 = new byte[_1MB * 4];
	}
	

}

5 空间分配担保

在老年代最大可用连续空间小于历次晋升到老年代对象的平均大小,也就是有风险的Minor GC

这个时候如果失败会进行Full GC,因此,此时设置HandlePromotionFailure其实就是为了避免Full GC过于频繁。

在JDK 6之后,规则变为,在老年代最大可用连续空间小于历次晋升到老年代对象的平均大小,就进行Full GC,不用设置。

参数

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-HandlePromotionFailure

代码

public class JvmTest {
	
	private static final int _1MB = 1024*1024;
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		byte[] allocation1,allocation2,allocation3,allocation4;  
		byte[] allocation5,allocation6,allocation7;
		allocation1 = new byte[_1MB * 2];
		allocation2 = new byte[_1MB * 2];
		allocation3 = new byte[_1MB * 2];
		allocation1 = null;
		allocation4 = new byte[_1MB * 2];
		allocation5 = new byte[_1MB * 2];
		allocation6 = new byte[_1MB * 2];
		allocation4 = null;
		allocation5 = null;
		allocation6 = null;
		
		allocation7 = new byte[_1MB * 2];
	}
	

}

在我的电脑上运行失败,无法识别HandlePromotionFailure

上一篇:【JVM】详解HotSpot、堆与OOM的原理并实现


下一篇:关于jvm中年轻代的参数设置