本條目大意
儘量避免在類中使用終結(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 方法一樣的功能。