1 先谈Finalize()
finalize()能做的所有工作,使用try-finally或者其他方式都可以做得更好、更及时,所以笔者建议大家完全可以忘掉Java语言中有这个方法的存在。
——《深入理解JVM》
finalize()方法确实可以实现一次对象的自救,但是其不确定性和昂贵的运行代价都表明这个方法的使用需要十分的慎重。那么finalize()在什么时期起作用又是如何实现对象的自救的呢?首先我们要理解虚拟机在扫描到死亡对象的时候并不是直接回收的,而是进行一次标记并且筛选,筛选的条件就是其对象的finalize方法是否有必要执行。如果当前对象没有重写finalize方法或者已经调用过一次finalize方法,那么则视为没有必要执行,此时便失去自救的机会,放入"即将回收"集合中。
否则的话,则将对象放入一个叫F-Queue的队列中,稍后虚拟机将一个个的执行队列中对象的finalize方法(就是在此处对象可以在finalize方法中将自身关联到引用链,从而暂时逃脱被回收的命运),需要注意的是虚拟机保证执行但不保证执行完finalize方法,原因是如果finalize方法执行时间过长或者陷入死循环,则可能让系统奔溃。全部执行之后,虚拟机将对队列的对象重新标记一次,如果还不在引用链中则GG,否则将其移出"即将回收"集合。下面例子参考《深入理解JVM》实现自救并且验证只能自救一次的过程。
public classTestForGc {/**定义一个根节点的静态变量*/
public staticTestForGc INSTANCE;/*** 重写finalize方法,让其被标记为有必要执行并且加入F-Q
*
*@throwsThrowable*/@Overrideprotected void finalize() throwsThrowable {super.finalize();
System.err.println("finalize method in TestForGc Class invoked!");//将自身关联到根节点中,实现自救
INSTANCE = this;
}public static void main(String[] args) throwsInterruptedException {
INSTANCE= newTestForGc();
INSTANCE= null;
System.gc();//睡眠1S,保证F-Q中的方法执行完毕
TimeUnit.SECONDS.sleep(1);if(Objects.nonNull(INSTANCE)) {
System.out.println("i successfully save myself by finalize method!");
}else{
System.out.println("i am dead :(");
}/** 下面验证finalize方法只能调用一次
* 几乎完全一样的代码,却是不同的结局*/INSTANCE= null;
System.gc();//睡眠