2012-09-17 17 views
5

比方說,我已經用close()方法創建了一些資源類來清理資源,我想覆蓋finalize()以釋放資源(並打印一個警告)如果有人忘記調用close()。這怎麼能正確完成?如何正確地實現一個終結器來檢測Java中的資源泄漏

  • 僅建議用於本地(JNI分配)資源嗎?
  • 如果從終結器中使用對已完成的另一個對象的引用,會發生什麼?如果存在循環依賴關係,我不會看到垃圾收集器如何阻止您訪問可能已執行終結器的對象。
  • 有沒有更好的替代方案來重寫finalize()來檢測和/或處理資源泄漏?
  • 執行終結器時需要注意的其他陷阱?

注意:我知道使用finalize()通常是個壞主意,並且不保證被調用,還有其他幾個問題在討論這個問題。這個問題具體是關於如何在Java中實現終結器,而不是約爲什麼你應該(或不應該)。

+0

我希望我的答案有幫助。讓我知道我是否錯過了任何東西。 –

+0

解釋了關於從終結器訪問可能已完成的對象的第二點。仍然好奇這會帶來什麼影響。 – Soulman

回答

4

effective java (2nd edition),約書亞在項目#7詳細說明你如何做到這一點。他首先建議你幾乎不應該使用finalizer s。但是,有一個原因使用它只打印日誌聲明,說你有資源泄漏。他說這樣做的一個缺點是,有人可以擴展你的班級,而不是正確地打電話給超級終結者。因此,他建議做這樣的事情在子類:

// Manual finalizer chaining 
    @Override protected void finalize() throws Throwable { 
     try { 
      ... // Finalize subclass state 
     } finally { 
      super.finalize(); 
    } 
} 

這是爲了確保,如果事情在當前類打破了finally仍然會被調用。這可能是一個糟糕的解決方案,因爲它取決於你的班級的子類。另一種解決方法是使用監護人目標文件來完成此操作。這看起來像:

// Finalizer Guardian idiom 
    public class Foo { 
// Sole purpose of this object is to finalize outer Foo object 
     private final Object finalizerGuardian = new Object() { 
     @Override protected void finalize() throws Throwable { 
      ... // Finalize outer Foo object 
     } 
     }; 
     ... // Remainder omitted 
} 

這是一個更簡潔的方法,因爲你知道,沒有人可以覆蓋該功能。

建議關閉資源的方法仍在實施Closeable並確保由用戶來關閉。正如Josuha所說,你不應該在finalize方法中做任何時間敏感的操作。 JVM可能會選擇在未來的某個時間運行它。如果你依靠這種方法來做提交或重要的事情,那麼這是一個壞主意。

1

它是否僅推薦用於本地(JNI分配)資源?

不可以。您的用例對終結器也是有效的。我的意思是記錄資源泄漏。

如果您在終結器中訪問另一個已經完成的 Java對象,會發生什麼?

如果您仍然可以訪問它,它還沒有最終確定。或者,也許我錯過了你的問題。

我現在明白了。可能是A和B都有資格進行垃圾回收,並且他們之間有一個參考。這應該是沒有問題的,因爲默認finalize()什麼都不做。如果您爲自己的對象編寫自定義finalize()方法,則應該編寫代碼而不依賴於它們的最終定單順序。您還應該警惕參照變爲null,因爲相應的對象可能已經被垃圾收集。

是否有更好的替代方案來檢測和/或處理資源泄漏的 的重寫finalize()?

我認爲使用終結器時最重要的是檢測和記錄/警告泄漏未處理它。記錄此泄漏的最佳時刻是在資源對象被垃圾收集之前。所以終結者自然適合這一點。

我想強調一下:我不會使用終結器來處理忘記關閉資源的程序員,而是告訴他他需要修復他的代碼。

執行終結器時需要注意的其他陷阱?

看起來對於有終結器的對象也會有性能損失。你應該做一個測試,看看它是如何爲你服務的。如果存在重要的性能損失,我會試着想象一種機制,僅在開發時使用終結器來記錄資源泄漏。

如Amir Raminfar所建議的那樣,在繼承帶有終結器的對象時要小心。

還有一件事:看看[​​或[FileOutputStream][2]源代碼出於同樣的原因使用終結器。

+0

「如果你仍然可以訪問它,它還沒有最終確定,或者我錯過了你的問題。」但是,如果對象A有一個對象B的引用,並且B在A之前完成,那麼不能從它的終結器訪問B? – Soulman

+0

編輯我的帖子並改寫了這個問題。 – Soulman

+0

我現在明白了,更新了答案。 – dcernahoschi