24

Effective Java說:爲什麼終結者有「嚴重的性能損失」?

使用終結器會有嚴重的性能損失。

爲什麼使用終結器銷燬對象會更慢?

+1

你可能喜歡這篇文章,它談論終結怎麼能可達再次使物體等,這也說明了爲什麼組合物可節省一天(而不是實現繼承)在相當長的一段情況:HTTP://java.sun。 com/developer/technicalArticles/javase/finalization/ – SyntaxT3rr0r 2010-05-19 00:13:01

回答

22

由於垃圾收集器的工作方式。爲了提高性能,大多數Java GC都使用複製收集器,在該收集器中將短期對象分配到「eden」內存塊中,並且在收集對象代時,GC需要複製對象仍然「活着」到更永久的存儲空間,然後它可以立即擦除(釋放)整個「eden」存儲塊。這很有效,因爲大多數Java代碼將創建數千個對象實例(盒裝基元,臨時數組等),其生命週期僅爲幾秒。

然而,當你在組合中有終結者時,GC不能一次簡單地擦掉整個一代。相反,它需要找出需要最終確定的那一代中的所有對象,並將它們排列在實際執行終結器的線程上。同時,GC無法高效地清理物體。所以它要麼讓它們活得比它們應該的時間更長,要麼必須延遲收集其他物體,或者兩者兼而有之。另外,您有實際執行終結器的任意等待時間。

所有這些因素加起來會導致運行時間損失,這就是爲什麼確定性定稿(使用方法或類似方法明確確定對象狀態)通常是首選。

+1

當然,這關注代代收藏家的問題。其他GC戰略有不同的問題。但是他們都歸結爲需要執行額外工作的GC,包括至少兩次通過一個對象以釋放它;一個將它添加到finalize隊列中,一個在finalization之後實際釋放它。 – 2010-05-19 02:01:14

+0

我認爲幾個常用的Java API類有終結器是否正確,以釋放O/S資源?我正在考慮'FileOutputStream'。因此,對於某些對象的終結器不太可能會延遲不使用終結器的對象的GC,因爲大多數程序都會受到影響。 – Raedwald 2014-08-01 10:52:37

+1

@Rededwald:對。例如,'FileOutputStream'的OpenJDK實現有一個終結器,你可以通過查看OpenJDK源碼來看到。 (儘管我無法找到任何*需要*標準庫實現來使用終結器)。因此,在實踐中,那些有資格獲得GC但尚未完成的對象只會被提升到下一代老一代(倖存者空間或終身),而終結者排隊等候運行。但是直到下一代收集下一代前輩時纔會回收實際的內存。 – 2014-08-01 15:09:14

1

我的想法是這樣的: Java是一種垃圾收集語言,它根據自己的內部算法釋放內存。每隔一段時間,GC都會掃描堆,確定哪些對象不再被引用,並取消分配內存。 終結器會中斷此操作並強制GC循環之外的內存釋放,從而可能導致效率低下。 我認爲最佳做法是僅在絕對必要時使用終結器,例如釋放文件句柄或關閉應確定性完成的數據庫連接。

+1

這是否真的迫使它,或僅僅是建議? – corsiKa 2010-05-18 18:58:06

+0

大部分是正確的,但終結器不會導致GC週期之外的內存重新分配。相反,如果GC確定一個對象需要最終確定,它會「復活」它並保持對象不被收集,直到終結器執行完畢。但可能需要一段時間,因爲(IIRC)終結者直到下一次收集終身代時才運行。 – 2010-05-18 19:04:06

+1

「我認爲最好的做法是僅在絕對必要時使用終結器,例如釋放文件句柄或關閉數據庫連接」:請注意,這恰恰是終結器不適用的地方,因爲終結器可能會遲到或根本不運行。 – sleske 2010-10-04 18:47:56

0

我能想到的一個原因是,如果您的資源都是Java對象而不是本地代碼,則顯式內存清理是不必要的。

1

我剛拿起我的副本有效的Java離開我的辦公桌,看看他指的是什麼。

如果您閱讀第2章第6節,他會詳細瞭解各種性能命中。

You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.

我會推薦閱讀部分的全部 - 它解釋了很多東西比我更可以在這裏鸚鵡。

1

如果仔細閱讀finalize()的文檔,您會注意到終結器可以防止被GC收集的對象。

如果不存在終結器,則該對象可以簡單地移除並且不需要更多關注。但是,如果有終結器,那麼之後需要檢查它,如果對象沒有再次變爲「可見」的話。

不知道當前的Java垃圾回收是如何實現的(實際上,因爲這裏有不同的Java實現,也有不同的GC),所以假設GC需要做一些額外的工作一個終結者,因爲這個特點。

9

實際上有碰上一個這樣的問題:

在Sun的HotSpot JVM,終結器上設定一個固定的,低優先級的線程處理。在高負載應用程序中,創建終結所需對象比低優先級終結線程可以處理它們更快。與此同時,掛起終止對象使用的堆空間不能用於其他用途。最終,您的應用程序可能會花費所有時間進行垃圾收集,因爲所有可用內存均由正在等待終結的對象使用。

當然,除了其他許多不使用有效Java中描述的終結器的原因之外。