第3章 垃圾收集器与内存分配策略

本文深入探讨Java垃圾回收机制中的引用计数算法、对象自我拯救、新生代与老年代分配策略、动态对象年龄判定及空间分配担保等核心概念。通过具体代码示例,展示不同场景下垃圾回收的行为与效果。

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

3.2.1 引用计数算法

/**
 * testGC()方法执行后,objA和objB会不会被GC呢?
 *VM Args:-verbose:gc: -Xms20M -Xmx20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8
 * @author huangh
 */
package cn.chapter3;

public class ReferenceCountingGC {
	
	public Object instance = null;
	
	private static final int _1MB = 1024*1024;
	
	/**
	 * 这个成员的唯一意义就是占点内存,以便能在GC日志中看清楚是否被回收过
	 */
	
	private byte[] bigSize = new byte[2*_1MB];
	
	public static void testGC(){
		ReferenceCountingGC objA = new ReferenceCountingGC();
		ReferenceCountingGC objB = new ReferenceCountingGC();
		objA.instance = objB;
		objB.instance = objA;
		
		objA = null;
		objB = null;
		
		//假设在这行发生GC,那么objA和objB是否能被回收?
		System.gc();
	}
	
	public static void main(String[] args){
		testGC();
	}
}

 书本有点坑啊(对新手),没有给出vm args和main函数

运行结果:

[Full GC[Tenured: 0K->375K(10240K), 0.0044187 secs] 4823K->375K(19456K), [Perm : 178K->178K(12288K)], 0.0207159 secs] [Times: user=0.00 sys=0.00, real=0.02 secs] 
Heap
 def new generation   total 9216K, used 166K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,   2% used [0x34000000, 0x34029b50, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800000, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 375K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,   3% used [0x34a00000, 0x34a5dd58, 0x34a5de00, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542cb90, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

4823K->375K(19456K)表明,GC发生前,该内存已使用4823K,GC发生后,该内存使用了375K,表明对象已被回收,从而表明JVM没有使用引用计数算法。Java中使用了可达性分析算法来来判定对象是否存活

 3.2.4 生存还是死亡

代码清单3-2 一次对象自我拯救的演示

/**
 * 此代码演示了两点
 * 1,对象可以在被GC时自我拯救
 * 2,这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次
 * @author huangh
 */
package cn.chapter3;

public class FinalizeEscapeGC {
	
	public static FinalizeEscapeGC SAVE_HOOK = null;
	
	public void isAlive(){
		System.out.println("yes, i am still alive:)");
	}
	
	@Override
	protected void finalize() throws Throwable{
		super.finalize();
		System.out.println("finalize method executed!");
		FinalizeEscapeGC.SAVE_HOOK = this;
	}
	
	public static void main(String[] args) throws Throwable {
		SAVE_HOOK = new FinalizeEscapeGC();
		
		//对象第一次成功拯救自己
		SAVE_HOOK = null;
		System.gc();
		//因为finalizer方法优先级很低,暂停0.5秒,以等待它
		Thread.sleep(500);
		if(SAVE_HOOK != null){
			SAVE_HOOK.isAlive();
		}else{
			System.out.println("no, i am dead:(");
		}
		
		//下面这段代码与上面完全相同,但是这次自救却失败了
		SAVE_HOOK = null;
		System.gc();
		//因为finalizer方法优先级很低,暂停0.5秒,以等待它
		Thread.sleep(500);
		if(SAVE_HOOK != null){
			SAVE_HOOK.isAlive();
		}else{
			System.out.println("no, i am dead:(");
		}
	}
}

运行 结果:

finalize method executed!
yes, i am still alive:)
no, i am dead:(

3.5.1 对象优先在Eden分配

书本上的vm args不全,少了-verbose:gc -XX:+PrintGCDetails,并且没有main函数

/**
 * 代码清单3-3, 新生代Minor GC
 * VM Args:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
 * @author huangh
 */
package cn.chapter3;

public class MinorGC {
	
	private static final int _1MB = 1024*1024;
	
	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]; //出现一次Minor GC
		
	}
	
	public static void main(String[] args){
		testAllocation();
	}
}

运行结果:

[GC[DefNew: 6871K->376K(9216K), 0.0069608 secs] 6871K->6520K(19456K), 0.0070398 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 def new generation   total 9216K, used 4638K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  52% used [0x34000000, 0x34429810, 0x34800000)
  from space 1024K,  36% used [0x34900000, 0x3495e098, 0x34a00000)
  to   space 1024K,   0% used [0x34800000, 0x34800000, 0x34900000)
 tenured generation   total 10240K, used 6144K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  60% used [0x34a00000, 0x35000030, 0x35000200, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542caf0, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

3.5.2 大对象直接进入老年代

书本上的vm args不全,少了-verbose:gc -XX:+PrintGCDetails,并且没有main函数

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
 * 3145728B表示3M,但是不能写成3M
 * @author huangh
 */
package cn.chapter3;

public class BigInstance {
	
	private static final int _1MB = 1024*1024;
	
	public static void testPretenureSizeThreshold(){
		byte[] allocation;
		allocation = new byte[4 * _1MB];//直接分配在老年代中
	}
	
	public static void main(String[] args) {
		testPretenureSizeThreshold();
	}
}

运行结果:

Heap
 def new generation   total 9216K, used 891K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  10% used [0x34000000, 0x340def78, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800000, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 4096K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  40% used [0x34a00000, 0x34e00010, 0x34e00200, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542cab0, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

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

书本上的vm args不全,少了-verbose:gc -XX:+PrintGCDetails,并且没有main函数

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution -XX:+PrintGCDetails
 */
package cn.chapter3;

public class LongSurvivor {
	
	private static final int _1MB = 1024*1024;
	
	@SuppressWarnings("unused")
	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();
	}
}

以MaxTenuringThreshold=1的参数设置类运行的结果

[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:     647384 bytes,     647384 total
: 5079K->632K(9216K), 0.0048008 secs] 5079K->4728K(19456K), 0.0048604 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 1)
- age   1:        136 bytes,        136 total
: 4812K->0K(9216K), 0.0010879 secs] 8908K->4727K(19456K), 0.0011262 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4260K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  52% used [0x34000000, 0x34428fd8, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800088, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 4727K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  46% used [0x34a00000, 0x34e9dec0, 0x34e9e000, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542caf0, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

以MaxTenuringThreshold=15的参数设置类运行的结果

[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     647384 bytes,     647384 total
: 5079K->632K(9216K), 0.0042640 secs] 5079K->4728K(19456K), 0.0043323 secs] [Times: user=0.00 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
: 4812K->0K(9216K), 0.0009837 secs] 8908K->4727K(19456K), 0.0010050 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4260K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  52% used [0x34000000, 0x34428fd8, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800088, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 4727K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  46% used [0x34a00000, 0x34e9dec0, 0x34e9e000, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542caf0, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

怎么运行结果时和书上不一样,没有出现age = 2的情况?这是什么原因???待解决。。。。

3.5.4 动态对象年龄判定

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintTenuringDistribution -XX:+PrintGCDetails
 */
package cn.chapter3;

public class LongSurvivor2 {
	
	private static final int _1MB = 1024*1024;
	
	@SuppressWarnings("unused")
	public static void testTenuringThreshold2(){
		byte[] allocation1, allocation2, allocation3,allocation4;
		allocation1 = new byte[_1MB/4]; //allocation1 + allocation2大于survivor空间的一半
		allocation2 = new byte[_1MB/4];
		allocation3 = new byte[4*_1MB];
		allocation4 = new byte[4*_1MB];
		allocation4 = null;
		allocation4 = new byte[4*_1MB];
	}
	
	public static void main(String[] args) {
		testTenuringThreshold2();
	}
}

运行结果:

[GC[DefNew
Desired survivor size 524288 bytes, new threshold 1 (max 15)
- age   1:     909552 bytes,     909552 total
: 5335K->888K(9216K), 0.0043895 secs] 5335K->4984K(19456K), 0.0044696 secs] [Times: user=0.00 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
: 5068K->0K(9216K), 0.0012928 secs] 9164K->4983K(19456K), 0.0013303 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 4260K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  52% used [0x34000000, 0x34428fd8, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800088, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 4983K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  48% used [0x34a00000, 0x34edded0, 0x34ede000, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542cb08, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

运行结果和书上的解释有误差

3.5.5 空间分配担保

/**
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:HandlePromotionFailure=false -XX:+PrintGCDetails
 */
package cn.chapter3;

public class handle_Promotion {
	
	private static final int _1MB = 1024*1024;
	
	@SuppressWarnings("unused")
	public static void testHandlePromotion(){
		byte[] allocation1, allocation2, allocation3,allocation4,allocation5,allocation6,allocation7;
		allocation1 = new byte[2*_1MB]; 
		allocation2 = new byte[2*_1MB];
		allocation3 = new byte[2*_1MB];
		allocation1 = null;
		allocation4 = new byte[2*_1MB]; 
		allocation5 = new byte[2*_1MB];
		allocation6 = new byte[2*_1MB];
		allocation1 = null; 
		allocation2 = null;
		allocation3 = null;
		allocation7 = new byte[2*_1MB];
	}
	
	public static void main(String[] args) {
		testHandlePromotion();
	}
}

以HandlePromotionFailure=false的参数设置来运行的结果

Java HotSpot(TM) Client VM warning: ignoring option HandlePromotionFailure=false; support was removed in 6.0_24
[GC[DefNew: 6871K->376K(9216K), 0.0043879 secs] 6871K->4472K(19456K), 0.0044752 secs] [Times: user=0.00 sys=0.02, real=0.02 secs] 
[GC[DefNew (promotion failed) : 6692K->6691K(9216K), 0.0036908 secs][Tenured: 8192K->6518K(10240K), 0.0046860 secs] 10788K->6518K(19456K), [Perm : 178K->178K(12288K)], 0.0084553 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 def new generation   total 9216K, used 2130K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  26% used [0x34000000, 0x34214828, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800000, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 6518K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  63% used [0x34a00000, 0x3505dab8, 0x3505dc00, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542cb58, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

以HandlePromotionFailure=true的参数设置来运行的结果

 

Java HotSpot(TM) Client VM warning: ignoring option HandlePromotionFailure=true; support was removed in 6.0_24
[GC[DefNew: 6871K->376K(9216K), 0.0038882 secs] 6871K->4472K(19456K), 0.0039691 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC[DefNew (promotion failed) : 6692K->6691K(9216K), 0.0037950 secs][Tenured: 8192K->6518K(10240K), 0.0047771 secs] 10788K->6518K(19456K), [Perm : 178K->178K(12288K)], 0.0086326 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
Heap
 def new generation   total 9216K, used 2130K [0x34000000, 0x34a00000, 0x34a00000)
  eden space 8192K,  26% used [0x34000000, 0x34214828, 0x34800000)
  from space 1024K,   0% used [0x34800000, 0x34800000, 0x34900000)
  to   space 1024K,   0% used [0x34900000, 0x34900000, 0x34a00000)
 tenured generation   total 10240K, used 6518K [0x34a00000, 0x35400000, 0x35400000)
   the space 10240K,  63% used [0x34a00000, 0x3505dab8, 0x3505dc00, 0x35400000)
 compacting perm gen  total 12288K, used 178K [0x35400000, 0x36000000, 0x39400000)
   the space 12288K,   1% used [0x35400000, 0x3542cb58, 0x3542cc00, 0x36000000)
    ro space 10240K,  44% used [0x39400000, 0x3987d3a8, 0x3987d400, 0x39e00000)
    rw space 12288K,  52% used [0x39e00000, 0x3a449a18, 0x3a449c00, 0x3aa00000)

运行结果应该时不一样的,但是运行时说好像被移除了,所以暂时看不出来区别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值