深入JVM GC对象的自救

Java虚拟机通过可达性分析等算法探测堆中不可达的对象,当一个对象通过可达性算法不可达时,并非立即被回收,而是暂时处于在“缓刑”状态,而真正要宣告一个对象死亡,必须经历至少2次标记过程

——如果对象在进行可达性分析后发现与GC Roots引用链不相连时,那么它会被第一次标记并且进行一次筛选,筛选的条件是此对象是否需要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机执行过,那么这2种情况都被视作没有必要执行。

——如果对象被判定需要执行finalize()方法,那么这个对象将会放置在一个F-Queue队列中,并在之后由虚拟机自动建立的,低优先级的Finalizer线程去执行队列中的finalize()方法。

——执行指的是虚拟机会触发对象的finalize()方法,但不承若等待它运行结束。这样做的目的是:一来对象可能在finalize()方法中执行缓慢,而来方法可能出现死循环,将会导致F-Queue队列中其它对象永远处于等待状态。导致整个内存分配体系崩溃

——finalize()方法是对象逃脱被GC回收的最后一次机会,之后GC对F-Queue中的对象进行第二次标记,如果对象在finalize()中成功自救(只要重新与GC Roots引用链关联,如把自己赋值给某个类变量或对象的成员变量,那么第二次标记时将移出回收集合)

如果这个对象没有逃脱,那么将会被GC回收

实例演示:

创建一个类,重写finalize方法  在方法内重新将对象引用到一个变量中

public class FinalizeEscapeGC {
	
	public  static  FinalizeEscapeGC SAVE_HOOK=null;
	
	public void isAlive()
	{
		System.out.println("still alive");
	}
	//重写finalize方法 使得本对象重新被变量引用
	@Override
	protected void finalize()throws Throwable
	{
		super.finalize();
		System.out.println("finalize method execute");
		FinalizeEscapeGC.SAVE_HOOK=this;
	}

	public static void main(String[] args) throws Throwable{
		
		System.out.println("test");
		
		// TODO Auto-generated method stub
		SAVE_HOOK=new FinalizeEscapeGC();
		//对象第一次自救
		SAVE_HOOK=null;
		System.gc();
		//finalize处于低优先级执行队列中
		Thread.sleep(500);
		if(SAVE_HOOK!=null)
			SAVE_HOOK.isAlive();
		else
		{
			System.out.println("is dead");
		}
		
		//以下代码重复执行  但是自救失败
		SAVE_HOOK=null;
		System.gc();
		//finalize处于低优先级执行队列中
		Thread.sleep(500);
		if(SAVE_HOOK!=null)
			SAVE_HOOK.isAlive();
		else
		{
			System.out.println("is dead");
		}
		

	}

}

运行结果:

第一次自救成功,第二次却失败了  因为对象的fianlize方法只会被执行一次 

finalize()方法是一个代价很高的函数,不确定性大,无法保证各个对象的执行调用顺序,

所以任何情况下都不推荐使用finalize()方法

猜你喜欢

转载自blog.csdn.net/qq_33369979/article/details/87885202