iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
Software Development

深入淺出Java 30天系列 第 8

Day 8: 避免使用finalizers

  • 分享至 

  • xImage
  •  

由於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


上一篇
Day 7: 清除陳舊的object references
下一篇
Day 9: 覆寫equals時的注意事項和規範(上)
系列文
深入淺出Java 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言