Effective Java 2nd Edition Reading Notes
Item7: Avoid Finalizers
不使用Finalizers。
Finalizers是不可预知的,有时是危险的,并且是不必要的。
在C++中,析构函数用于回收资源,而在Java中,通过GC来回收资源。析构函数还用于回收非内存资源,在Java中,通过try{}finally{}来完成同样的工作。
首先finalizers并不能保证被马上执行。在对象不可用开始到finalize方法执行之间的时间是任意的。这意味着不能在finalizers中进行具有时间要求的操作。例如在finalizer中关闭文件。[操作系统中可以打开的文件数是有限制的,一旦超过限制,将不能再打开文件。]
何时执行finalizers是GC的功能,因JVM的实现不同而不同。使用finalizers在极端的情况下会导致实例回收的延迟,从而发生OutOfMemoryError。规范没有定义finalizers什么时候执行,甚至不能确保finalizers是否会被执行。
永远不要依赖finalizers去实现持久性的更新。例如在finalizers中释放持久性的锁。
不要使用System.gc或者System.runFinalization,它们导致finalizers的运行怪异,并且不提供任何保障。
另外,如果未捕获的异常发生在finalizers中,那么异常被忽略(不输出任何信息),finalizer将终止。
同时,使用finalizers会带来严重的性能影响。
Oh, and one more thing: there is a severe performance penalty for using finalizers. On my machine, the time to create and destroy a simple object is about 5.6 ns. Adding a finalizer increases the time to 2,400 ns. In other words, it is about 430 times slower to create and destroy objects with finalizers.
进行finalize的一个好的办法是提供一个显式的终止方法,同时记录实例的状态,例如提供一个成员变量来记录,在使用其他方法的时候,检查该成员变量的值。如果实例已经被终止,那么抛出IllegalStateException。比较典型的是java.sql.Connection, java.io.InputStream, java.io.OutputStream, java.util.Timer等等。
显式的终止方法通常和try{}finally{}结合使用,在finally中调用终止方法来终止对象。
finalizer的实际作用:
Finalizer的一个作用是提供一个Safety net,即万一客户端,例如使用InputStream的其他类忘记关闭流,那么通过finalize方法来关闭,但是最好应该在finalize方法中提供日志输出,提示客户端错误。
例如FileOutputStream的finalize方法如下:
/**
* Cleans up the connection to the file, and ensures that the
* <code>close</code> method of this file output stream is
* called when there are no more references to this stream.
*
* @exception IOException if an I/O error occurs.
* @see java.io.FileInputStream#close()
*/
protected void finalize() throws IOException {
if (fd != null) {
if (fd == fd.out || fd == fd.err) {
flush();
} else {
close();
}
}
}
Finalizer的另一个作用适用于本地对等对象(java对象通过本地对等对象获取),由于native peer不是java实例,所以GC不会回收对等对象。此时提供finalizer来回收该对象。