什么是Metaspace的壓縮類空間?

在64位平臺上,hotspot使用稱為壓縮對象指針(“CompressedOops”)和壓縮類指針的優(yōu)化技術(shù)。兩者都是同一事物的變體。

壓縮指針是一種引用數(shù)據(jù)(Java堆中的對象或元空間中的類元數(shù)據(jù))的方法,即使在64位平臺上也使用32位引用。

這有許多優(yōu)點,例如指針大小更小,從而減少內(nèi)存占用和更好地利用緩存,并且在某些平臺上可以使用更多的寄存器。

Note: A good explanation of Compressed Object Pointers can be found here: JVM Anatomy Quark #23: Compressed References.

Also, a similar motivation drives the Linux x32 abi.

因為最終一個人需要一個64位的地址來訪問那個東西,那個32位的“指針”實際上是一個偏移量——可能是位移位——進(jìn)入一個具有已知公共基的區(qū)域。

關(guān)于Metaspace,我們不關(guān)心壓縮的oop,但必須處理壓縮類指針:

每個Java對象的頭中都有一個對Metaspace中Java堆之外的本機(jī)結(jié)構(gòu)的引用:Class結(jié)構(gòu)。

什么是metaspace的壓縮類空間?

使用壓縮類指針時,該引用是32位值。為了找到該結(jié)構(gòu)的真正64位地址,我們向其添加一個已知的公共基,并可能將值左移三位:

什么是metaspace的壓縮類空間?

該技術(shù)對如何分配這些Klass結(jié)構(gòu)設(shè)置了技術(shù)限制:

Klass結(jié)構(gòu)的每個可能的位置必須在4G(非移位模式)| 32G(移位模式)的范圍內(nèi),以從公共基址1的32位偏移量可到達(dá)。

這兩個限制意味著我們需要將元空間分配為一個連續(xù)的區(qū)域。

當(dāng)通過malloc(3)mmap(3)這樣的系統(tǒng)API從系統(tǒng)分配內(nèi)存時,地址由系統(tǒng)選擇,并且可以是適合類型范圍的任何值。因此,在64位平臺上,當(dāng)然不能保證后續(xù)分配會在范圍限制內(nèi)產(chǎn)生地址。E、 g.一個mmap(3)調(diào)用可以映射到0x0000000700000000,一個映射到0x0000000f0000000。

因此,我們必須使用一個mmap()調(diào)用來建立Klass對象的區(qū)域。因此,我們需要預(yù)先知道這個區(qū)域的大小,它不能大于32G,也永遠(yuǎn)不能可靠地擴(kuò)展,因為超出其末端的地址范圍可能已經(jīng)被占用。

這些限制是嚴(yán)厲的。它們也只是真正需要用于Klass結(jié)構(gòu),而不是用于其他類元數(shù)據(jù):目前只有Klass實例被壓縮引用處理。因此,可以將其他64位指針放在任何位置。

因此決定將元空間分成兩部分:“非類部分”和“類部分”:

  • 等級部分,包括Class結(jié)構(gòu),必須分配為一個不大于32G的連續(xù)區(qū)域。
  • 包含其他所有內(nèi)容的非類部分則沒有。

Terminology: The class part is called “Compressed Class Space” even though that is a bit of a misnomer since the Klass structures themselves are not compressed but the pointers to them.

壓縮類空間的大小由-XX:CompressedClassSpaceSize決定。因為我們需要預(yù)先知道類空間的大小,所以該參數(shù)不能為空。如果省略,則默認(rèn)為1GB。

更令人困惑的是,hotspot人為地將classspacesize壓縮到3G的最大值——我真的不知道為什么。因此,除了32G的技術(shù)限制之外,我們還人為地設(shè)置了3G的限制。

另外請注意,我們一直在談?wù)撎摂M尺寸,而不是漫畫尺寸。這種記憶只有在需要的時候才會提交。非常簡化,虛擬大小在大多數(shù)現(xiàn)代操作系統(tǒng)上幾乎不需要任何成本,它只是一個addres空間預(yù)留。

由于Klass結(jié)構(gòu)的平均大小為1K,一個默認(rèn)大小為1G的壓縮類空間將能夠容納大約一百萬個Klass結(jié)構(gòu)(參見調(diào)整元空間大小)。這是我們可以加載的類數(shù)量的唯一實際限制。

還請注意,當(dāng)我們不使用CompressedOops運行時,compressedClasspointer將被禁用。如果我們通過-XX:-CompressedOops手動關(guān)閉CompressedOops,或者Java堆大于或等于32G,就會發(fā)生這種情況。

往期參考:http://javakk.com/160.html

Implementation

為了重用現(xiàn)有的元空間實現(xiàn),采用了一個技巧:

全局結(jié)構(gòu)VirtualSpaceList和ChunkManager都是重復(fù)的,現(xiàn)在存在于兩個變體中,“類空間”變量和“非類空間”變量。

但是由于類空間需要一個連續(xù)的地址范圍,我們不能真正使用映射區(qū)域鏈;因此類空間列表退化了:它只包含一個節(jié)點,不能增長。與非類列表中的同類節(jié)點相比,這個節(jié)點是巨大的。這個節(jié)點就是壓縮的類空間。

什么是metaspace的壓縮類空間?
什么是metaspace的壓縮類空間?

ClassLoaderMetaspace——每個類裝入器結(jié)構(gòu)都包含使用這個類裝入器的塊——現(xiàn)在需要兩個鏈接的塊列表,一個用于保存非類塊,另一個用于類塊。這也意味著我們將當(dāng)前節(jié)點的“空閑”部分加倍,因為現(xiàn)在我們有兩個節(jié)點。

開關(guān):UseCompressedClassPointers、UseCompressedOops

  • -XX:+UseCompressedOops啟用壓縮對象指針。

  • -XX:+UseCompressedClassPointers啟用壓縮類指針。

默認(rèn)情況下,兩者都處于打開狀態(tài),但可以手動關(guān)閉。

如果壓縮類指針被關(guān)閉,我們將沒有壓縮的類空間,并且-XX:CompressedClassSpaceSize開關(guān)將被忽略。

-XX:+UseCompressedClassPointers需要-XX:+useCompressedDoops,但反之亦然:可以在沒有壓縮類指針的情況下運行壓縮oops。這可能有助于在一些病態(tài)的角落案例中減少元空間內(nèi)存占用。一般來說,建議不要使用這些開關(guān)。

注意,壓縮對象指針需要Java堆<32G。因此,如果Java堆>=32G,壓縮oop將被關(guān)閉,這也將關(guān)閉壓縮類指針。

文章來源:http://javakk.com/405.html
也歡迎大家關(guān)注我的公眾號【Java老K】獲取更多干貨

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容