如何逃脱垃圾回收

关于垃圾回收的触发机制,参考MinorGC和FullGC触发条件,本篇就是从其扩展而来。

判断一个对象是否存活,除了GC Roots引用之外,还有一个条件就是对象是否重写了finalize方法,如果对象重写了该方法,则会交给FQueue队列去执行,如果执行该方法后被重新关联,则在下次回收时不会被回收,否则下次回收,该方法只执行一次。

注:如果在finalize方法里面被重新引用,则下次GC不会被回收。但是如果再次失去引用,则无论如何都会在下次GC时被回收,因为finalize方法只执行一次。

测试代码:

	private static final int _1M = 1024 * 1024;

	private static List<LargeObjectOverrideFinalizeEscapeCollect> escapeObjs = new ArrayList<LargeObjectOverrideFinalizeEscapeCollect>();

	private static void testMinorGCOverriddeFinalizeEscapeCollect() {

		/* Eden区为8M,from/to space各为1M */
		LargeObjectOverrideFinalizeEscapeCollect largeOb1 = new LargeObjectOverrideFinalizeEscapeCollect(_1M * 1 / 2,
				"largeOb1");
		LargeObjectOverrideFinalizeEscapeCollect largeOb2 = new LargeObjectOverrideFinalizeEscapeCollect(_1M * 1,
				"largeOb2");
		LargeObjectOverrideFinalizeEscapeCollect largeOb3 = new LargeObjectOverrideFinalizeEscapeCollect(_1M * 2,
				"largeOb3");
		largeOb1 = null;
		largeOb3 = null;
		LargeObjectOverrideFinalizeEscapeCollect largeOb4 = new LargeObjectOverrideFinalizeEscapeCollect(_1M * 3,
				"largeOb4");
		LargeObjectOverrideFinalizeEscapeCollect largeOb5 = new LargeObjectOverrideFinalizeEscapeCollect(_1M * 2,
				"largeOb5");

		/* 保证调用finalize完毕 */
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.gc();

		escapeObjs.clear();
		System.out.println("=============clear escape objects.");
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}


		System.gc();
	}

	public static void main(String[] agrs) {
		testMinorGCOverriddeFinalizeEscapeCollect();
	}

	static class LargeObjectOverrideFinalizeEscapeCollect {
		private byte[] data;
		private String name;

		public LargeObjectOverrideFinalizeEscapeCollect(int size, String name) {
			data = new byte[size];
			this.name = name;
			System.out.println(
					"Over Constructing LargeObjectOverrideFinalizeEscapeCollect " + name + System.lineSeparator());
		}

		public String getName() {
			return name;
		}

		@Override
		public void finalize() {
			System.out.println("Finalize LargeObjectOverrideFinalizeEscapeCollect " + name + " , release "
					+ (data.length / 1024) + "K.");
			escapeObjs.add(this);
		}
	}

测试环境:JDK1.8.0_144,Java HotSpot(TM) 64-Bit Server VM,默认垃圾收集器为PS Scavenge+PS MarkSweep。

-Xms30m -Xmx30m -Xmn10m -XX:+PrintGCDetails -XX:+PrintHeapAtGC     

堆总大小为30m,新生代为10m,打印GC信息,在GC前后打印内存信息。

当前垃圾处理器装置

打印结果及分析:

Over Constructing LargeObjectOverrideFinalizeEscapeCollect largeOb1

Over Constructing LargeObjectOverrideFinalizeEscapeCollect largeOb2

Over Constructing LargeObjectOverrideFinalizeEscapeCollect largeOb3

Over Constructing LargeObjectOverrideFinalizeEscapeCollect largeOb4

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 9216K, used 7804K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 95% used [0x00000000ff600000,0x00000000ffd9f030,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 20480K, used 0K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 0% used [0x00000000fe200000,0x00000000fe200000,0x00000000ff600000)
 Metaspace       used 2867K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K

// 由于重写了finalize,所以第一次GC时largeOb1和largeOb3没有被回收。
[GC (Allocation Failure) [PSYoungGen: 7804K->664K(9216K)] 7804K->7328K(29696K), 0.0044152 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 9216K, used 664K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 64% used [0x00000000ffe00000,0x00000000ffea6030,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 20480K, used 6664K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 32% used [0x00000000fe200000,0x00000000fe882040,0x00000000ff600000)
 Metaspace       used 2867K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K
}
Finalize LargeObjectOverrideFinalizeEscapeCollect largeOb1 , release 512K.
Over Constructing LargeObjectOverrideFinalizeEscapeCollect largeOb5

Finalize LargeObjectOverrideFinalizeEscapeCollect largeOb3 , release 2048K.
{Heap before GC invocations=2 (full 0):
 PSYoungGen      total 9216K, used 3036K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 28% used [0x00000000ff600000,0x00000000ff851140,0x00000000ffe00000)
  from space 1024K, 64% used [0x00000000ffe00000,0x00000000ffea6030,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 20480K, used 6664K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 32% used [0x00000000fe200000,0x00000000fe882040,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K

// 由于被重新关联,所以第二次GC时largeOb1和largeOb3也没有被回收
[GC (System.gc()) [PSYoungGen: 3036K->656K(9216K)] 9700K->9368K(29696K), 0.0036913 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
Heap after GC invocations=2 (full 0):
 PSYoungGen      total 9216K, used 656K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 64% used [0x00000000fff00000,0x00000000fffa4040,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 20480K, used 8712K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 42% used [0x00000000fe200000,0x00000000fea82050,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=3 (full 1):
 PSYoungGen      total 9216K, used 656K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 64% used [0x00000000fff00000,0x00000000fffa4040,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 20480K, used 8712K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 42% used [0x00000000fe200000,0x00000000fea82050,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K

// 由于被重新关联,所以第一次Full GC时largeOb1和largeOb3没有被回收。
[Full GC (System.gc()) [PSYoungGen: 656K->0K(9216K)] [ParOldGen: 8712K->9264K(20480K)] 9368K->9264K(29696K), [Metaspace: 2868K->2868K(1056768K)], 0.0148887 secs] [Times: user=0.06 sys=0.00, real=0.02 secs] 
Heap after GC invocations=3 (full 1):
 PSYoungGen      total 9216K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 20480K, used 9264K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 45% used [0x00000000fe200000,0x00000000feb0c128,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K
}
=============clear escape objects.
{Heap before GC invocations=4 (full 1):
 PSYoungGen      total 9216K, used 160K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 1% used [0x00000000ff600000,0x00000000ff6281a8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
  to   space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
 ParOldGen       total 20480K, used 9264K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 45% used [0x00000000fe200000,0x00000000feb0c128,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K

// 这次GC只有新生代的GC,老年代没有GC,但是又叫Full GC,比较疑惑?
[GC (System.gc()) [PSYoungGen: 160K->32K(9216K)] 9424K->9296K(29696K), 0.0005980 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap after GC invocations=4 (full 1):
 PSYoungGen      total 9216K, used 32K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 3% used [0x00000000ffe00000,0x00000000ffe08000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 20480K, used 9264K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 45% used [0x00000000fe200000,0x00000000feb0c128,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K
}
{Heap before GC invocations=5 (full 2):
 PSYoungGen      total 9216K, used 32K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 3% used [0x00000000ffe00000,0x00000000ffe08000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 20480K, used 9264K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 45% used [0x00000000fe200000,0x00000000feb0c128,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K

// 由于清理了引用,所以第二次Full GC时largeOb1和largeOb3被回收
[Full GC (System.gc()) [PSYoungGen: 32K->0K(9216K)] [ParOldGen: 9264K->6704K(20480K)] 9296K->6704K(29696K), [Metaspace: 2868K->2868K(1056768K)], 0.0095751 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap after GC invocations=5 (full 2):
 PSYoungGen      total 9216K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 20480K, used 6704K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 32% used [0x00000000fe200000,0x00000000fe88c148,0x00000000ff600000)
 Metaspace       used 2868K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 311K, capacity 386K, committed 512K, reserved 1048576K
}
Heap
 PSYoungGen      total 9216K, used 164K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 2% used [0x00000000ff600000,0x00000000ff6290d0,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 20480K, used 6704K [0x00000000fe200000, 0x00000000ff600000, 0x00000000ff600000)
  object space 20480K, 32% used [0x00000000fe200000,0x00000000fe88c148,0x00000000ff600000)
 Metaspace       used 2874K, capacity 4490K, committed 4864K, reserved 1056768K
  class space    used 312K, capacity 386K, committed 512K, reserved 1048576K
有一个小问题:为什么full 1有两次,第二次full 1只清理了新生代空间?

如果不执行escapeObjs.clear();,则对象都不会被回收。

猜你喜欢

转载自blog.csdn.net/shi2huang/article/details/80077843