避免使用finalizer

Java语言规范里没有要求finalizer何时被执行,甚至没要求它一定要被执行。各种不同的jvm实现完全可能以它们自己的方式来处理finalizer,所以finalizer里的代码执行没有任何保障,从这点上来说,很多c++程序员把finalizer作为c++析构函数在Java中的对应功能是完全错误的。如果错误地依赖finalizer来释放被占用的资源或者执行某些时序任务,很可能会造成不可预料的错误行为。

Java面试里面一个烂大街的问题就是System.gc()方法有什么作用,其实还有一个类似的方法System.runFinalization(),两者没太大区别,都是向jvm提出gc请求,但根本无法保证jvm会及时作出响应。当然,这两个方法还是提高了finalizer被执行的可能性。

finalizer的另一个问题是异常处理。当其中的代码抛出异常时,如果没有显式定义catch处理,这些异常将被完全忽略,所在线程会瞬间终止,连stack trace都不会打印。

最后,finalizer会造成严重的性能损失。比如正常情况下销毁一个简单对象耗时5.6ns,定义一个finalizer之后,这个耗时会增加到2400ns,变慢了430倍。

Java中正确释放资源的方法,应该是显式定义一个terminate方法,并在finally模块中调用它。而且,最好在此方法中将对象状态定义为"已终止",以避免不恰当地使用不完整对象:

// try-finally block guarantees execution of termination method
Foo foo = new Foo(...);
try {
// Do what must be done with foo
...
} finally {
	foo.terminate();	// Explicit termination method
}

只有两种情况下需要使用finalizer方法:

1、作为显式terminate方法的双保险

2、涉及到本地对象(native object)的释放

在真正使用finalizer的场合,需要注意finalizer方法不像构造函数那样有chaining,也就是说,在一个子类覆盖了父类的finalizer时,必须显式调用super.finalize(),否则后者不会被自动执行:

// Manual finalizer chaining
@Override protected void finalize() throws Throwable {
	try {
	... // Finalize subclass state
	} finally {
		super.finalize();
	}
}

还有一种解决方法是finalizer guardian:

// Finalizer Guardian idiom
public class Foo {
// Sole purpose of this object is to finalize outer Foo object
private final Object finalizerGuardian = new Object() {
		@Override protected void finalize() throws Throwable {
		... // Finalize outer Foo object
		}
	};
	... // Remainder omitted
}

当Foo子类的一个对象被终结时,finalizerGuardian作为它的一个成员也会被终结,其覆盖的finalize()方法会被调用,所以不管Foo的子类是否显式调用了super.finalize(),Foo都会被妥善终结。

提交评论


安全码
刷新