JEP 421: Java將要終結finalize()了!

banq發表於2021-11-02

finalize()在未來的版本中,預設情況下將被禁用,在以後的版本中它將被刪除。依賴於最終確定的庫和應用程式的維護者應該考慮遷移到其他資源管理技術,例如try-with-resources 語句cleaners

Java 程式能自動記憶體管理,其中 JVM 的垃圾收集器 (GC) 在不再需要物件時回收該物件使用的記憶體。但是,某些物件表示作業系統提供的資源,例如開啟的檔案描述符或本機記憶體塊。對於這樣的物件,僅僅回收物件的記憶體是不夠的;程式還必須將底層資源釋放回作業系統,通常是透過呼叫物件的close方法。如果程式在 GC 回收物件之前未能執行此操作,則釋放資源所需的資訊將丟失。作業系統仍認為正在使用的資源已洩漏。
ava 1.0 中引入的finalize()旨在幫助避免資源洩漏:一個個類可以宣告一個終結方法:protected void finalize();
該finalize方法可以執行諸如呼叫物件的close方法之類的操作。乍一看,這似乎是防止資源洩漏的有效安全網,實際上,finalize()利用垃圾收集的力量來管理非記憶體資源(Barry Hayes,收集器介面中的終結,記憶體管理國際研討會,1992)。
不幸的是,最終確定有幾個關鍵的基本缺陷:
  • 不可預測的延遲——在物件變得不可訪問的那一刻和它的finalize被呼叫的那一刻之間可能會經過任意長的時間。事實上,GC 不保證任何finalize都會被呼叫。
  • 不受約束的行為——finalize程式碼可以採取任何行動。特別是,它可以儲存對正在終結的物件的引用,從而復活該物件並使其再次可達。
  • 始終啟用— finalize沒有明確的序號產生器制。帶有finalize的類可以對類的每個例項進行終結,無論是否需要。不能取消物件的終結,即使該物件不再需要它。
  • 未指定的執行緒——finalize以任意順序在未指定的執行緒上執行。執行緒和排序都無法控制。

這些缺陷在二十多年前就被廣泛認可。它在 Java 平臺中的存在給整個生態系統帶來了負擔,因為它將所有庫和應用程式程式碼暴露在安全性、可靠性和效能風險中。它還對 JDK,特別是 GC 實現施加了持續的維護和開發成本。為了推動 Java 平臺向前發展,我們將棄用finalize以進行移除。
鑑於與最終確定相關的問題,開發人員應該使用替代技術來避免資源洩漏,即try-with-resources 和清潔器。
  • Try-with-resources — Java 7 引入了try-with-resources 語句,作為對上述try-finally結構的改進。
    try-with-resources 正確處理所有異常情況,避免了對finalize安全網的需要。在單個詞法範圍內開啟和關閉的任何資源都應轉換為與try-with-resources 一起使用。如果帶有finalize的類的例項只能在try-with-resources 語句中使用,則finalize可能是不必要的,可以刪除。
  • Cleaner ——有些資源的生命try週期太長,無法與-with-resources很好地配合使用,因此 Java 9 引入了CleanerAPI 來幫助釋放它們。Cleaner API 允許程式為物件註冊清理操作,該操作在物件變得不可訪問後的一段時間執行。清理操作避免了finalize器的許多缺點:

但是,與終結器一樣,清理操作是由 GC 安排的,因此它們可能會受到無限延遲的影響。因此,在需要及時釋放資源的情況下,不應使用更​​清潔的 API。
 
將最終棄用java.base和java.desktop模組中的這些方法

java.lang.Object.finalize()
java.lang.Enum.finalize()
java.awt.Graphics.finalize()
java.awt.PrintJob.finalize()
java.util.concurrent.ThreadPoolExecutor.finalize()
javax.imageio.spi.ServiceRegistry.finalize()
javax.imageio.stream.FileCacheImageInputStream.finalize()
javax.imageio.stream.FileImageInputStream.finalize()
javax.imageio.stream.FileImageOutputStream.finalize()
javax.imageio.stream.ImageInputStreamImpl.finalize()
javax.imageio.stream.MemoryCacheImageInputStream.finalize()

此外,我們將:
  • 最終棄用java.lang.Runtime.runFinalization()和java.lang.System.runFinalization(). 這些方法沒有最終確定就沒有任何意義。
  • 棄用模組介面中的getObjectPendingFinalizationCount()方法

相關文章