Effective Java (2nd Edition)读书笔记-Item 7: 避免finalizers
Finalizer是不可预知的,经常出问题的,而且是不必要的。使用它可能会导致不可预知的行为,性能降低和简洁问题。C++需要析构函数释放资源,Java是用垃圾回收器管理对象释放。Java中一般在finally中是否其他资源。
finalizer的一个缺点是:不能保证他们在对象无用的时候立即执行。这就意味着不能在finalizer中做严格要求时间的事情。例如不要在finalizer中关闭文件。
finalizer的执行只是垃圾回收器算法的一个主要函数。每个JVM实现都会不同。
不要依赖finalizer来更新临界持有状态。例如不要在finalizer中释放共享资源的持有锁。
不要受System.gc和System.runFinalizerOnExit的诱惑.它们可能会增加finalizer执行的机会,但是不能保证。System.runFinalizerOnExit和Runtime.runFinalizersOnExit只是保证finalization。
使用finalizers会导致性能损失。在我的机器上创建和销毁一个对象的时间是5.6ns,但是使用finalizer会增加时间到2400ns.
那么应该如何终止资源,例如文件和线程?如果仅仅是需要显式的终止方法,那么可以使用InputStream和OutputStream和java.sql.Connection的Close方法。还有就是使用java.util.Timer的Cancel方法,对于java.awt使用Graphics.dispose和Window.dispose.
一般典型的显式终止方法是包含在try-finally中来确保结束。例如
1
//
try-finally block guarantees execution of termination method
2 Foo foo = new Foo();
3 try {
4// Do what must be done with foo
5
6} finally {
7foo.terminate(); // Explicit termination method
8}
四个类来作为显示终止方法模式的例子:FileInputStream, FileOutputStream, Timer, and Connection。
2 Foo foo = new Foo();
3 try {
4// Do what must be done with foo
5
6} finally {
7foo.terminate(); // Explicit termination method
8}
使用finalizer的第二个合理对象是native peers。native peer是一个native对象它是通过native方法来代理正常的对象。由于native peer不是正常的对象,垃圾回收器无法知道它和在Java Peer回收的时候回收它。就需要finalizer来完成这个任务,来确保native peer不再持有临界资源。
应该注意到,“finalizer chaining”不是自动完成的。如果一个类有一个finalizer,子类复写了它。子类必须手动调用父类的finalizer。应该包含子类的finalizer在try中,将父类的finalizer放在finally中:
//
Manual finalizer chaining
@Override protected void finalize() throws Throwable {
try {
// Finalize subclass state
} finally {
super.finalize();
}
}
如果子类实现复写了父类的finalizer,但是忘记了调用,父类的finalizer就不会被调用。
@Override protected void finalize() throws Throwable {
try {
// Finalize subclass state
} finally {
super.finalize();
}
}
可以将finalizer放入一个匿名类中。匿名类的单实例叫做finalizer哨,被包含类为每个实例创建一个finalizer哨。
1
//
Finalizer Guardian idiom
2 public class Foo {
3// Sole purpose of this object is to finalize outer Foo object
4private final Object finalizerGuardian = new Object() {
5@Override protected void finalize() throws Throwable {
6 // Finalize outer Foo object
7}
8};
9 // Remainder omitted
10}
包含类存储了一个私有单一引用在finalizer哨中。注意,Foo类不需要finalizer,因此不用担心是否子类调用super.finalize.这种技术应该在每个具有finalizer的非final public 类中考虑。
2 public class Foo {
3// Sole purpose of this object is to finalize outer Foo object
4private final Object finalizerGuardian = new Object() {
5@Override protected void finalize() throws Throwable {
6 // Finalize outer Foo object
7}
8};
9 // Remainder omitted
10}
总结:
不要使用finalizer,除非为了安全网或者终止非临界native资源。在那些使用finalizer的极少的实例中,记住要调用super.finalize。如果使用finalize作为安全网,记住从finalizer中输出无效用法的日志。最后,如果你需要关联一个finalizer到public非静态类中,考虑使用finalizer哨,它可以保证调用super.finalize失败是还是可以finalization。
In summary, don’t use finalizers except as a safety net or to terminate
noncritical native resources. In those rare instances where you do use a finalizer,
remember to invoke super.finalize. If you use a finalizer as a safety net,
remember to log the invalid usage from the finalizer. Lastly, if you need to
associate a finalizer with a public, nonfinal class, consider using a finalizer
guardian, so finalization can take place even if a subclass finalizer fails to invoke
super.finalize.