由於finalizers不可預測,不知道什麼時候才會被執行,容易造成需要被清除資源未被清除,有潛在的效能問題,所以必須避免使用。舉例來說,開檔之後最好要立刻關檔,但如果把關檔的動作放在finalizer,有可能會發生開了一堆檔案但都沒關,結果最後要開檔的時候開不了。
而且finalizers的執行優先度是比較低的,甚至是一直沒有機會被執行,有可能等到開始執行的時候,某些使用到的物件或reference已經不見了,finalizers存取的時候出現錯誤。所以切勿把釋放共用的資源的步驟,放到finalizers執行,像是資料庫的persistent lock。
因為上面描述的種種缺點,如果需要在程式最後執行某個功能,建議還是使用try-with-resources ,在try-catch 後面加finally 會比較好,下面的範例示範了finalizers和try-with-resources 兩個不同寫法,並在兩個方法開始執行和結束時紀錄時間。
class WithFinalizer {
public void performTask() {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("WithFinalizer: Sum is " + sum);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
long endTime = System.currentTimeMillis();
System.out.println("Time end time with finalizer: " + endTime + " ms");
}
}
class WithoutFinalizer {
public void performTask() {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("WithoutFinalizer: Sum is " + sum);
}
public void cleanUp() {
System.out.println("WithoutFinalizer: Cleaning up.");
}
}
public class FinalizerPerformance {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
System.out.println("Time start time with finalizer: " + startTime + " ms");
WithFinalizer wf = new WithFinalizer();
wf.performTask();
wf = null;
System.gc();
startTime = System.currentTimeMillis();
System.out.println("Time start time WithoutFinalizer: " + startTime + " ms");
WithoutFinalizer wof = null;
try {
wof = new WithoutFinalizer();
wof.performTask();
} finally {
if (wof != null) {
wof.cleanUp();
}
long endTime = System.currentTimeMillis();
System.out.println("Time end time WithoutFinalizer: " + endTime + " ms");
}
}
}
執行之後,程式裡面的Time start time WithoutFinalizer是放在Time end time with finalizer後面,但是Time start time WithoutFinalizer卻先被執行,驗證前面說的finalizers不可預測。執行時間,finalizers的部分是6 ms,try-catch-finally 是3 ms,只有finalizers的一半。
因為finalizers有各種不確定性,所以使用finalizers最好還是加上log,避免執行過程中有錯誤,無法debug。而在實作finalize() 的時候,應使用try-with-resources 做包覆,並且在finally 裡面執行父類別的finalize(),這樣可以確保父類別finalize()裡面做的事也會被執行。
class WithFinalizer {
// Finalizer guardian pattern
private final Object finalizerGuardian = new Object() {
@Override
protected void finalize() throws Throwable {
// 父類別的程式碼
System.out.println("FinalizerGuardianExample: Finalizer guardian finalize method called.");
}
};
public void performTask() {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println("WithFinalizer: Sum is " + sum);
}
@Override
protected void finalize() throws Throwable {
try {
long endTime = System.currentTimeMillis();
System.out.println("Time end time with finalizer: " + endTime + " ms");
} finally {
super.finalize();
}
}
}
最後,finalizers在Java 9已經被標示為deprecated,所以盡量別在使用。
Reference:
https://www.ithome.com.tw/voice/129618