2015-05-08 46 views
9

關於Java中可終結對象的討論通常討論當可終結對象(及其關聯資源)無法快速垃圾收集時發生的常見間接成本。一個對象可以終結的前期成本是多少?

我現在更感興趣的是,在內存條件和對象分配時間中,實際可終止的直接成本是多少。我已經看到了在一些地方這種成本的存在,拐彎抹角例如,Oracle's article on finalization memory retention issues注:

obj分配,在JVM內部記錄了obj是終結。這通常會減慢現代JVM具有的快速分配路徑。

JVM如何記錄一個對象實例是否可終止,以及這樣做的內存和性能成本如何?

對於那些有興趣在我的具體應用:

我們生產和保留數百萬難以置信的輕量級的對象;向這些對象添加單個指針的代價非常高昂,所以我們已經做了一些工作來刪除指針,而不是使用打包到字段子集中的較小數字ID。將數字解包允許從使用Map存儲它們的Pool中檢索具有該ID的共享不變屬性。

剩下的問題是如何處理不再使用的屬性值的垃圾回收。

一個已經考慮的策略是使用引用計數;當創建對象並檢索某個值的共用標識時,該值的引用計數會遞增;當它不再使用時,它必須遞減。確保這一點遞減

一種選擇的情況是添加下面的finalize方法:

public void finalize() { 
    Pool.release(getPropertyId()); 
} 

但是,如果被終結的這個行爲意味着對對象的額外指針必須保持,上箭頭這個應用程序的前端成本可以被認爲是高的。如果這意味着必須分配額外的對象,那麼它幾乎肯定會太高......因此,我的問題是:可定稿的直接前期成本是多少?

+0

是不是不好的做法,依靠的對象的終結?我的意思是,它不能保證'finalize()'會被調用 - 或者? – vikingsteve

+0

您可能想要管理自己的對象池,而不是依靠JVM。根據您需要同時使用多少個對象,您可以通過完全避免分配/垃圾收集/最終化開銷來發現巨大的性能提升。 –

+0

您不應該創建這麼多的可終結對象,這應該是一個問題。您應該只將它作爲最後的手段使用,否則清理對象時會發現嚴重的性能/穩定性問題。你可能不會擔心,但是使用這個很多,你會。 –

回答

7

終結者是糟糕不僅因爲保留問題,而且從性能角度來看。

在Oracle JDK/OpenJDK中,使用finalize方法的對象由Finalizerjava.lang.ref.Reference的子類)的實例支持。

所有終結器在對象構造器的末尾分兩步註冊:調用from Java to VM,然後調用。 JIT編譯器不能內聯這種雙轉換Java-> VM-> Java。但最糟糕的是,Finalizer的構造函數在global lock下創建了一個鏈表! (facepalm)

終結器在內存佔用方面也不好:除了所有引用字段,它們還有two extra fieldsnextprev

PhantomReferences比終結好得多:

  • 他們施工時不需要過渡到VM和背部可內聯的;
  • 除了繼承java.lang.ref.Reference之外,它們沒有額外的字段;
  • 沒有進行全局同步。

This benchmark比較終結對象的分配速度和物體的幻影支持:

Benchmark    Mode Cnt  Score  Error Units 
Finalizer.finalizable thrpt 5 2171,312 ± 1469,705 ops/ms 
Finalizer.phantom  thrpt 5 61280,612 ± 692,922 ops/ms 
Finalizer.plain  thrpt 5 225752,307 ± 7618,304 ops/ms 
+0

是否有任何理由不修補openJDK中的全局鎖定? – qwwdfsad

+0

@qwwdfsad我會說沒有補丁的理由。爲什麼要關心不推薦使用的遺留代碼。 – apangin

相關問題