本条目大意
尽量避免在类中使用终结(finalize)方法,在里面写一些释放类中资源的语句。
为什么要避免使用 finalize方法?
1、java语言规范不仅不保证 finalize方法会被及时地执行,而且根本不保证他们会被执行。
2、System.gc 和 System.runFinalization 这两个方法只是增加了finalizer 方法被执行的机会。
3、唯一能保证 finalize 方法被执行的方法有两个,System.runFinalizersOnExit 和 Runtime.runFinalizersOnExit ,但是这两个方法已经被废弃。
4、覆盖并使用终结方法会有严重的性能损失。
5、及时地执行终结方法是垃圾回收算法的一个主要功能,但是在不同的JVM实现中会大相庭径,使用终结方法可能会丧失平台无关性。
如果类的对象中封装的资源确实需要进行释放,我们应该怎么做呢?
一般来说,需要释放资源的有线程或者文件还有涉及到本地资源的对象。
我们不去覆盖 finalize 方法,而是自己提供一个显式的终止资源的方法。比如 java.io.FileInputStream 的close 方法。当使用完这个FileInputStream对象时,显式调用close() 来回收资源。
我们首先看看 FileInputStream 是怎么玩的:
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
if (channel != null) {
fd.decrementAndGetUseCount();
channel.close();
}
int useCount = fd.decrementAndGetUseCount();
if ((useCount <= 0) || !isRunningFinalize()) {
close0();
}
}
要求此类的使用者不再使用此类的时候调用 close 终止方法,进行释放资源,并且在类中添加一个 closed 来标记资源是否已经释放,如果已经被释放了,那此类中的方法如果再被调用的话就抛出异常。
抽象形式如下:
class MyObject{
private boolean isClosed = false;
//终止方法
public void close(){
//资源释放操作...
isClosed = true;
}
}
public static void main(String... args) {
MyObject object = new MyObject();
try{
//在这里使用调用方法...
} finally {
//在这里关闭
object.close();
}
}
终结方法的用途
再继续往下看,就会看到 FileInputStream 还是有覆盖 finalize方法的。
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
runningFinalize.set(Boolean.TRUE);
try {
close();
} finally {
runningFinalize.set(Boolean.FALSE);
}
}
}
清楚的看到方法里面进行调用了 close 方法,这是为了当对象持有者忘记调用前面段落中建议的显式终结方法 :close()的情况下,使用inalize 方法充当“安全网”这是终结方法是用途之一。
抽象形式如下:
class MyObject{
private boolean isClosed = false;
//终止方法
public void close(){
//资源释放操作...
isClosed = true;
}
//安全网
protected void finalize() throws Throwable {
try{
close();
} finally {
super.finalize();
}
}
}
终结方法还有一个好处,把终结方法放在一个匿名的类,该匿名类的唯一用途就是终结它的外围实例。
外围实例持有对终结方法守卫者的唯一实例,这意味着当外围实例是不可达时,这个终结方法守卫者也是不可达的了,垃圾回收器回收外围实例的同时也会回收终结方法守卫者的实例,而终结方法守卫者的finalize方法就把外围实例的资源释放掉,就好像是终结方法是外围实例的一个方法一样。
看看 java.util.Timer 的终结方法守卫者:
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); // In case queue was already empty.
}
}
private final Object threadReaper = new Object() {
protected void finalize() throws Throwable {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.notify(); // In case queue is empty.
}
}
};
cancel 方法是 Timer 提供的显式终止方法,threadReaper 是一个私有变量,保证除了实例本身外没有对它的引用,它覆盖 finalize 方法,实现与 cancel 方法一样的功能。