×

消息

EU e-Privacy Directive

This website uses cookies to manage authentication, navigation, and other functions. By using our website, you agree that we can place these types of cookies on your device.

View e-Privacy Directive Documents

You have declined cookies. This decision can be reversed.

避免使用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都会被妥善终结。

提交评论


安全码
刷新