在64位平臺(tái)上,hotspot使用稱(chēng)為壓縮對(duì)象指針(“CompressedOops
”)和壓縮類(lèi)指針的優(yōu)化技術(shù)川陆。兩者都是同一事物的變體剂习。
壓縮指針是一種引用數(shù)據(jù)(Java堆中的對(duì)象或元空間中的類(lèi)元數(shù)據(jù))的方法,即使在64位平臺(tái)上也使用32位引用较沪。
這有許多優(yōu)點(diǎn)鳞绕,例如指針大小更小,從而減少內(nèi)存占用和更好地利用緩存购对,并且在某些平臺(tá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.
因?yàn)樽罱K一個(gè)人需要一個(gè)64位的地址來(lái)訪問(wèn)那個(gè)東西,那個(gè)32位的“指針”實(shí)際上是一個(gè)偏移量——可能是位移位——進(jìn)入一個(gè)具有已知公共基的區(qū)域骡苞。
關(guān)于Metaspace垂蜗,我們不關(guān)心壓縮的oop,但必須處理壓縮類(lèi)指針:
每個(gè)Java對(duì)象的頭中都有一個(gè)對(duì)Metaspace中Java堆之外的本機(jī)結(jié)構(gòu)的引用:Class結(jié)構(gòu)解幽。
使用壓縮類(lèi)指針時(shí),該引用是32位值躲株。為了找到該結(jié)構(gòu)的真正64位地址片部,我們向其添加一個(gè)已知的公共基,并可能將值左移三位:
該技術(shù)對(duì)如何分配這些Klass結(jié)構(gòu)設(shè)置了技術(shù)限制:
Klass結(jié)構(gòu)的每個(gè)可能的位置必須在4G(非移位模式)| 32G(移位模式)的范圍內(nèi)档悠,以從公共基址1的32位偏移量可到達(dá)。
這兩個(gè)限制意味著我們需要將元空間分配為一個(gè)連續(xù)的區(qū)域望浩。
當(dāng)通過(guò)malloc(3)
或mmap(3)
這樣的系統(tǒng)API從系統(tǒng)分配內(nèi)存時(shí)辖所,地址由系統(tǒng)選擇,并且可以是適合類(lèi)型范圍的任何值磨德。因此缘回,在64位平臺(tái)上吆视,當(dāng)然不能保證后續(xù)分配會(huì)在范圍限制內(nèi)產(chǎn)生地址。E酥宴、 g.一個(gè)mmap(3)調(diào)用可以映射到0x0000000700000000啦吧,一個(gè)映射到0x0000000f0000000。
因此拙寡,我們必須使用一個(gè)mmap()
調(diào)用來(lái)建立Klass對(duì)象的區(qū)域授滓。因此,我們需要預(yù)先知道這個(gè)區(qū)域的大小倒庵,它不能大于32G褒墨,也永遠(yuǎn)不能可靠地?cái)U(kuò)展炫刷,因?yàn)槌銎淠┒说牡刂贩秶赡芤呀?jīng)被占用擎宝。
這些限制是嚴(yán)厲的。它們也只是真正需要用于Klass結(jié)構(gòu)浑玛,而不是用于其他類(lèi)元數(shù)據(jù):目前只有Klass實(shí)例被壓縮引用處理绍申。因此,可以將其他64位指針?lè)旁谌魏挝恢谩?/p>
因此決定將元空間分成兩部分:“非類(lèi)部分”和“類(lèi)部分”:
- 等級(jí)部分顾彰,包括Class結(jié)構(gòu)极阅,必須分配為一個(gè)不大于32G的連續(xù)區(qū)域。
- 包含其他所有內(nèi)容的非類(lèi)部分則沒(mé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.
壓縮類(lèi)空間的大小由-XX:CompressedClassSpaceSize
決定筋搏。因?yàn)槲覀冃枰A(yù)先知道類(lèi)空間的大小,所以該參數(shù)不能為空厕隧。如果省略奔脐,則默認(rèn)為1GB。
更令人困惑的是吁讨,hotspot人為地將classspacesize壓縮到3G的最大值——我真的不知道為什么髓迎。因此,除了32G的技術(shù)限制之外建丧,我們還人為地設(shè)置了3G的限制排龄。
另外請(qǐng)注意,我們一直在談?wù)撎摂M尺寸翎朱,而不是漫畫(huà)尺寸橄维。這種記憶只有在需要的時(shí)候才會(huì)提交。非常簡(jiǎn)化拴曲,虛擬大小在大多數(shù)現(xiàn)代操作系統(tǒng)上幾乎不需要任何成本争舞,它只是一個(gè)addres空間預(yù)留。
由于Klass結(jié)構(gòu)的平均大小為1K疗韵,一個(gè)默認(rèn)大小為1G的壓縮類(lèi)空間將能夠容納大約一百萬(wàn)個(gè)Klass結(jié)構(gòu)(參見(jiàn)調(diào)整元空間大卸艺稀)。這是我們可以加載的類(lèi)數(shù)量的唯一實(shí)際限制。
還請(qǐng)注意流译,當(dāng)我們不使用CompressedOops運(yùn)行時(shí)逞怨,compressedClasspointer將被禁用。如果我們通過(guò)-XX:-CompressedOops
手動(dòng)關(guān)閉CompressedOops福澡,或者Java堆大于或等于32G叠赦,就會(huì)發(fā)生這種情況。
往期參考:http://javakk.com/160.html
Implementation
為了重用現(xiàn)有的元空間實(shí)現(xiàn)革砸,采用了一個(gè)技巧:
全局結(jié)構(gòu)VirtualSpaceList和ChunkManager都是重復(fù)的除秀,現(xiàn)在存在于兩個(gè)變體中,“類(lèi)空間”變量和“非類(lèi)空間”變量算利。
但是由于類(lèi)空間需要一個(gè)連續(xù)的地址范圍册踩,我們不能真正使用映射區(qū)域鏈;因此類(lèi)空間列表退化了:它只包含一個(gè)節(jié)點(diǎn)效拭,不能增長(zhǎng)暂吉。與非類(lèi)列表中的同類(lèi)節(jié)點(diǎn)相比,這個(gè)節(jié)點(diǎn)是巨大的缎患。這個(gè)節(jié)點(diǎn)就是壓縮的類(lèi)空間慕的。
ClassLoaderMetaspace——每個(gè)類(lèi)裝入器結(jié)構(gòu)都包含使用這個(gè)類(lèi)裝入器的塊——現(xiàn)在需要兩個(gè)鏈接的塊列表肮街,一個(gè)用于保存非類(lèi)塊,另一個(gè)用于類(lèi)塊判导。這也意味著我們將當(dāng)前節(jié)點(diǎn)的“空閑”部分加倍嫉父,因?yàn)楝F(xiàn)在我們有兩個(gè)節(jié)點(diǎn)。
開(kāi)關(guān):UseCompressedClassPointers骡楼、UseCompressedOops
-XX:+UseCompressedOops
啟用壓縮對(duì)象指針熔号。-XX:+UseCompressedClassPointers
啟用壓縮類(lèi)指針。
默認(rèn)情況下鸟整,兩者都處于打開(kāi)狀態(tài)引镊,但可以手動(dòng)關(guān)閉。
如果壓縮類(lèi)指針被關(guān)閉篮条,我們將沒(méi)有壓縮的類(lèi)空間弟头,并且-XX:CompressedClassSpaceSize
開(kāi)關(guān)將被忽略。
-XX:+UseCompressedClassPointers
需要-XX:+useCompressedDoops
涉茧,但反之亦然:可以在沒(méi)有壓縮類(lèi)指針的情況下運(yùn)行壓縮oops赴恨。這可能有助于在一些病態(tài)的角落案例中減少元空間內(nèi)存占用。一般來(lái)說(shuō)伴栓,建議不要使用這些開(kāi)關(guān)伦连。
注意雨饺,壓縮對(duì)象指針需要Java堆<32G。因此惑淳,如果Java堆>=32G额港,壓縮oop將被關(guān)閉,這也將關(guān)閉壓縮類(lèi)指針歧焦。
文章來(lái)源:http://javakk.com/405.html
也歡迎大家關(guān)注我的公眾號(hào)【Java老K】獲取更多干貨