JVM完全解讀之Metaspace解密源碼分析
概述
metaspace,顧名思義,元數(shù)據(jù)空間,專門用來存元數(shù)據(jù)的,它是jdk8里特有的數(shù)據(jù)結(jié)構(gòu)用來替代perm,這塊空間很有自己的特點(diǎn),前段時(shí)間公司這塊的問題太多了,主要是因?yàn)樯?jí)了中間件所致,看到大家討論來討論去,看得出很多人對(duì)metaspace還是模棱兩可,不是很了解它,因此我覺得有必要寫篇文章來介紹一下它,解開它神秘的面紗,當(dāng)我們?cè)俅闻龅剿南嚓P(guān)問題的時(shí)候不會(huì)再感到束手無策。
通過這篇文章,你將可以了解到
- 為什么會(huì)有metaspace
- metaspace的組成
- metaspace的VM參數(shù)
- jstat里我們應(yīng)該關(guān)注metaspace的哪些值
為什么會(huì)有metaspace
metaspace的由來民間已有很多傳說,不過我這里只談我自己的理解,因?yàn)槲也皇莖racle參與這塊的開發(fā)者,所以對(duì)其真正的由來不怎么了解。
我們都知道jdk8之前有perm這一整塊內(nèi)存來存klass等信息,我們的參數(shù)里也必不可少地會(huì)配置-XX:PermSize以及-XX:MaxPermSize來控制這塊內(nèi)存的大小,jvm在啟動(dòng)的時(shí)候會(huì)根據(jù)這些配置來分配一塊連續(xù)的內(nèi)存塊,但是隨著動(dòng)態(tài)類加載的情況越來越多,這塊內(nèi)存我們變得不太可控,到底設(shè)置多大合適是每個(gè)開發(fā)者要考慮的問題,如果設(shè)置太小了,系統(tǒng)運(yùn)行過程中就容易出現(xiàn)內(nèi)存溢出,設(shè)置大了又總感覺浪費(fèi),盡管不會(huì)實(shí)質(zhì)分配這么大的物理內(nèi)存?;谶@么一個(gè)可能的原因,于是metaspace出現(xiàn)了,希望內(nèi)存的管理不再受到限制,也不要怎么關(guān)注元數(shù)據(jù)這塊的OOM問題,雖然到目前來看,也并沒有完美地解決這個(gè)問題。
或許從JVM代碼里也能看出一些端倪來,比如MaxMetaspaceSize默認(rèn)值很大,CompressedClassSpaceSize默認(rèn)也有1G,從這些參數(shù)我們能猜到metaspace的作者不希望出現(xiàn)它相關(guān)的OOM問題。
metaspace的組成
metaspace其實(shí)由兩大部分組成
- Klass Metaspace
- NoKlass Metaspace
Klass Metaspace就是用來存klass的,klass是我們熟知的class文件在jvm里的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),不過有點(diǎn)要提的是我們看到的類似A.class其實(shí)是存在heap里的,是java.lang.Class的一個(gè)對(duì)象實(shí)例。這塊內(nèi)存是緊接著Heap的,和我們之前的perm一樣,這塊內(nèi)存大小可通過-XX:CompressedClassSpaceSize參數(shù)來控制,這個(gè)參數(shù)前面提到了默認(rèn)是1G,但是這塊內(nèi)存也可以沒有,假如沒有開啟壓縮指針就不會(huì)有這塊內(nèi)存,這種情況下klass都會(huì)存在NoKlass Metaspace里,另外如果我們把-Xmx設(shè)置大于32G的話,其實(shí)也是沒有這塊內(nèi)存的,因?yàn)闀?huì)這么大內(nèi)存會(huì)關(guān)閉壓縮指針開關(guān)。還有就是這塊內(nèi)存最多只會(huì)存在一塊。
NoKlass Metaspace專門來存klass相關(guān)的其他的內(nèi)容,比如method,constantPool等,這塊內(nèi)存是由多塊內(nèi)存組合起來的,所以可以認(rèn)為是不連續(xù)的內(nèi)存塊組成的。這塊內(nèi)存是必須的,雖然叫做NoKlass Metaspace,但是也其實(shí)可以存klass的內(nèi)容,上面已經(jīng)提到了對(duì)應(yīng)場景。
Klass Metaspace和NoKlass Mestaspace都是所有classloader共享的,所以類加載器們要分配內(nèi)存,但是每個(gè)類加載器都有一個(gè)SpaceManager,來管理屬于這個(gè)類加載的內(nèi)存小塊。如果Klass Metaspace用完了,那就會(huì)OOM了,不過一般情況下不會(huì),NoKlass Mestaspace是由一塊塊內(nèi)存慢慢組合起來的,在沒有達(dá)到限制條件的情況下,會(huì)不斷加長這條鏈,讓它可以持續(xù)工作。
metaspace的幾個(gè)參數(shù)
如果我們要改變metaspace的一些行為,我們一般會(huì)對(duì)其相關(guān)的一些參數(shù)做調(diào)整,因?yàn)閙etaspace的參數(shù)本身不是很多,所以我這里將涉及到的所有參數(shù)都做一個(gè)介紹,也許好些參數(shù)大家都是有誤解的
- UseLargePagesInMetaspace
- InitialBootClassLoaderMetaspaceSize
- MetaspaceSize
- MaxMetaspaceSize
- CompressedClassSpaceSize
- MinMetaspaceExpansion
- MaxMetaspaceExpansion
- MinMetaspaceFreeRatio
- MaxMetaspaceFreeRatio
UseLargePagesInMetaspace
默認(rèn)false,這個(gè)參數(shù)是說是否在metaspace里使用LargePage,一般情況下我們使用4KB的page size,這個(gè)參數(shù)依賴于UseLargePages這個(gè)參數(shù)開啟,不過這個(gè)參數(shù)我們一般不開。
InitialBootClassLoaderMetaspaceSize
64位下默認(rèn)4M,32位下默認(rèn)2200K,metasapce前面已經(jīng)提到主要分了兩大塊,Klass Metaspace以及NoKlass Metaspace,而NoKlass Metaspace是由一塊塊內(nèi)存組合起來的,這個(gè)參數(shù)決定了NoKlass Metaspace的第一個(gè)內(nèi)存Block的大小,即2*InitialBootClassLoaderMetaspaceSize,同時(shí)為bootstrapClassLoader的第一塊內(nèi)存chunk分配了InitialBootClassLoaderMetaspaceSize的大小
MetaspaceSize
默認(rèn)20.8M左右(x86下開啟c2模式),主要是控制metaspaceGC發(fā)生的初始閾值,也是最小閾值,但是觸發(fā)metaspaceGC的閾值是不斷變化的,與之對(duì)比的主要是指Klass Metaspace與NoKlass Metaspace兩塊committed的內(nèi)存和。
MaxMetaspaceSize
默認(rèn)基本是無窮大,但是我還是建議大家設(shè)置這個(gè)參數(shù),因?yàn)楹芸赡軙?huì)因?yàn)闆]有限制而導(dǎo)致metaspace被無止境使用(一般是內(nèi)存泄漏)而被OS Kill。這個(gè)參數(shù)會(huì)限制metaspace(包括了Klass Metaspace以及NoKlass Metaspace)被committed的內(nèi)存大小,會(huì)保證committed的內(nèi)存不會(huì)超過這個(gè)值,一旦超過就會(huì)觸發(fā)GC,這里要注意和MaxPermSize的區(qū)別,MaxMetaspaceSize并不會(huì)在jvm啟動(dòng)的時(shí)候分配一塊這么大的內(nèi)存出來,而MaxPermSize是會(huì)分配一塊這么大的內(nèi)存的。
CompressedClassSpaceSize
默認(rèn)1G,這個(gè)參數(shù)主要是設(shè)置Klass Metaspace的大小,不過這個(gè)參數(shù)設(shè)置了也不一定起作用,前提是能開啟壓縮指針,假如-Xmx超過了32G,壓縮指針是開啟不來的。如果有Klass Metaspace,那這塊內(nèi)存是和Heap連著的。
MinMetaspaceExpansion
MinMetaspaceExpansion和MaxMetaspaceExpansion這兩個(gè)參數(shù)或許和大家認(rèn)識(shí)的并不一樣,也許很多人會(huì)認(rèn)為這兩個(gè)參數(shù)不就是內(nèi)存不夠的時(shí)候,然后擴(kuò)容的最小大小嗎?其實(shí)不然
這兩個(gè)參數(shù)和擴(kuò)容其實(shí)并沒有直接的關(guān)系,也就是并不是為了增大committed的內(nèi)存,而是為了增大觸發(fā)metaspace GC的閾值
這兩個(gè)參數(shù)主要是在比較特殊的場景下救急使用,比如gcLocker或者should_concurrent_collect的一些場景,因?yàn)檫@些場景下接下來會(huì)做一次GC,相信在接下來的GC中可能會(huì)釋放一些metaspace的內(nèi)存,于是先臨時(shí)擴(kuò)大下metaspace觸發(fā)GC的閾值,而有些內(nèi)存分配失敗其實(shí)正好是因?yàn)檫@個(gè)閾值觸頂導(dǎo)致的,于是可以通過增大閾值暫時(shí)繞過去
默認(rèn)332.8K,增大觸發(fā)metaspace GC閾值的最小要求。假如我們要救急分配的內(nèi)存很小,沒有達(dá)到MinMetaspaceExpansion,但是我們會(huì)將這次觸發(fā)metaspace GC的閾值提升MinMetaspaceExpansion,之所以要大于這次要分配的內(nèi)存大小主要是為了防止別的線程也有類似的請(qǐng)求而頻繁觸發(fā)相關(guān)的操作,不過如果要分配的內(nèi)存超過了MaxMetaspaceExpansion,那MinMetaspaceExpansion將會(huì)是要分配的內(nèi)存大小基礎(chǔ)上的一個(gè)增量
MaxMetaspaceExpansion
默認(rèn)5.2M,增大觸發(fā)metaspace GC閾值的最大要求。假如說我們要分配的內(nèi)存超過了MinMetaspaceExpansion但是低于MaxMetaspaceExpansion,那增量是MaxMetaspaceExpansion,如果超過了MaxMetaspaceExpansion,那增量是MinMetaspaceExpansion加上要分配的內(nèi)存大小
注:每次分配只會(huì)給對(duì)應(yīng)的線程一次擴(kuò)展觸發(fā)metaspace GC閾值的機(jī)會(huì),如果擴(kuò)展了,但是還不能分配,那就只能等著做GC了
MinMetaspaceFreeRatio
MinMetaspaceFreeRatio和下面的MaxMetaspaceFreeRatio,主要是影響觸發(fā)metaspaceGC的閾值
默認(rèn)40,表示每次GC完之后,假設(shè)我們?cè)试S接下來metaspace可以繼續(xù)被commit的內(nèi)存占到了被commit之后總共committed的內(nèi)存量的MinMetaspaceFreeRatio%,如果這個(gè)總共被committed的量比當(dāng)前觸發(fā)metaspaceGC的閾值要大,那么將嘗試做擴(kuò)容,也就是增大觸發(fā)metaspaceGC的閾值,不過這個(gè)增量至少是MinMetaspaceExpansion才會(huì)做,不然不會(huì)增加這個(gè)閾值
這個(gè)參數(shù)主要是為了避免觸發(fā)metaspaceGC的閾值和gc之后committed的內(nèi)存的量比較接近,于是將這個(gè)閾值進(jìn)行擴(kuò)大
一般情況下在gc完之后,如果被committed的量還是比較大的時(shí)候,換個(gè)說法就是離觸發(fā)metaspaceGC的閾值比較接近的時(shí)候,這個(gè)調(diào)整會(huì)比較明顯
注:這里不用gc之后used的量來算,主要是擔(dān)心可能出現(xiàn)committed的量超過了觸發(fā)metaspaceGC的閾值,這種情況一旦發(fā)生會(huì)很危險(xiǎn),會(huì)不斷做gc,這應(yīng)該是jdk8在某個(gè)版本之后才修復(fù)的bug
MaxMetaspaceFreeRatio
默認(rèn)70,這個(gè)參數(shù)和上面的參數(shù)基本是相反的,是為了避免觸發(fā)metaspaceGC的閾值過大,而想對(duì)這個(gè)值進(jìn)行縮小。這個(gè)參數(shù)在gc之后committed的內(nèi)存比較小的時(shí)候并且離觸發(fā)metaspaceGC的閾值比較遠(yuǎn)的時(shí)候,調(diào)整會(huì)比較明顯
jstat里的metaspace字段
我們看GC是否異常,除了通過GC日志來做分析之外,我們還可以通過jstat這樣的工具展示的數(shù)據(jù)來分析,前面我公眾號(hào)里有篇文章介紹了jstat這塊的實(shí)現(xiàn),有興趣的可以到我的公眾號(hào)你假笨里去翻閱下jstat的這篇文章。
我們通過jstat可以看到metaspace相關(guān)的這么一些指標(biāo),分別是M,CCS,MC,MU,CCSC,CCSU,MCMN,MCMX,CCSMN,CCSMX
它們的定義如下:
column {
header "^M^" /* Metaspace - Percent Used */
data (1-((sun.gc.metaspace.capacity - sun.gc.metaspace.used)/sun.gc.metaspace.capacity)) * 100
align right
width 6
scale raw
format "0.00"
}
column {
header "^CCS^" /* Compressed Class Space - Percent Used */
data (1-((sun.gc.compressedclassspace.capacity - sun.gc.compressedclassspace.used)/sun.gc.compressedclassspace.capacity)) * 100
align right
width 6
scale raw
format "0.00"
}
column {
header "^MC^" /* Metaspace Capacity - Current */
data sun.gc.metaspace.capacity
align center
width 6
scale K
format "0.0"
}
column {
header "^MU^" /* Metaspae Used */
data sun.gc.metaspace.used
align center
width 6
scale K
format "0.0"
}
column {
header "^CCSC^" /* Compressed Class Space Capacity - Current */
data sun.gc.compressedclassspace.capacity
width 8
align right
scale K
format "0.0"
}
column {
header "^CCSU^" /* Compressed Class Space Used */
data sun.gc.compressedclassspace.used
width 8
align right
scale K
format "0.0"
}
column {
header "^MCMN^" /* Metaspace Capacity - Minimum */
data sun.gc.metaspace.minCapacity
scale K
align right
width 8
format "0.0"
}
column {
header "^MCMX^" /* Metaspace Capacity - Maximum */
data sun.gc.metaspace.maxCapacity
scale K
align right
width 8
format "0.0"
}
column {
header "^CCSMN^" /* Compressed Class Space Capacity - Minimum */
data sun.gc.compressedclassspace.minCapacity
scale K
align right
width 8
format "0.0"
}
column {
header "^CCSMX^" /* Compressed Class Space Capacity - Maximum */
data sun.gc.compressedclassspace.maxCapacity
scale K
align right
width 8
format "0.0"
}
我這里對(duì)這些字段分類介紹下
MC & MU & CCSC & CCSU
MC表示Klass Metaspace以及NoKlass Metaspace兩者總共committed的內(nèi)存大小,單位是KB,雖然從上面的定義里我們看到了是capacity,但是實(shí)質(zhì)上計(jì)算的時(shí)候并不是capacity,而是committed,這個(gè)是要注意的
MU這個(gè)無可厚非,說的就是Klass Metaspace以及NoKlass Metaspace兩者已經(jīng)使用了的內(nèi)存大小
CCSC表示的是Klass Metaspace的已經(jīng)被commit的內(nèi)存大小,單位也是KB
CCSU表示Klass Metaspace的已經(jīng)被使用的內(nèi)存大小
M & CCS
M表示的是Klass Metaspace以及NoKlass Metaspace兩者總共的使用率,其實(shí)可以根據(jù)上面的四個(gè)指標(biāo)算出來,即(CCSU+MU)/(CCSC+MC)
CCS表示的是NoKlass Metaspace的使用率,也就是CCSU/CCSC算出來的
PS:所以我們有時(shí)候看到M的值達(dá)到了90%以上,其實(shí)這個(gè)并不一定說明metaspace用了很多了,因?yàn)閮?nèi)存是慢慢commit的,所以我們的分母是慢慢變大的,不過當(dāng)我們committed到一定量的時(shí)候就不會(huì)再增長了
MCMN & MCMX & CCSMN & CCSMX
MCMN和CCSMN這兩個(gè)值大家可以忽略,一直都是0
MCMX表示Klass Metaspace以及NoKlass Metaspace兩者總共的reserved的內(nèi)存大小,比如默認(rèn)情況下Klass Metaspace是通過CompressedClassSpaceSize這個(gè)參數(shù)來reserved 1G的內(nèi)存,NoKlass Metaspace默認(rèn)reserved的內(nèi)存大小是2* InitialBootClassLoaderMetaspaceSize
CCSMX表示Klass Metaspace reserved的內(nèi)存大小
綜上所述,其實(shí)看metaspace最主要的還是看MC,MU,CCSC,CCSU這幾個(gè)具體的大小來判斷metaspace到底用了多少更靠譜
以上就是JVM完全解讀之Metaspace解密源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Metaspace源碼解密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java Testcontainers庫實(shí)現(xiàn)測試功能
這篇文章主要介紹了Java Testcontainers庫實(shí)現(xiàn)測試功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java中JDom解析XML_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
JDOM是一種解析XML的Java工具包。DOM適合于當(dāng)今流行的各種語言,包括Java,JavaScripte,VB,VBScript,Perl,C,C++等。下面通過本文給大家介紹Java中JDom解析XML的方法,感興趣的朋友一起學(xué)習(xí)吧2017-07-07
Java?Chassis3熔斷機(jī)制的改進(jìn)路程技術(shù)解密
這篇文章主要介紹了Java?Chassis?3技術(shù)解密之熔斷機(jī)制的改進(jìn)路程實(shí)例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
java 中函數(shù)的參數(shù)傳遞詳細(xì)介紹
這篇文章主要介紹了 java 中函數(shù)的參數(shù)傳遞詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-11-11
Java正則驗(yàn)證字串符RegexValidator類使用
正則驗(yàn)證字串符是一種強(qiáng)大的工具,可以幫助程序員在處理字符串時(shí)輕松進(jìn)行復(fù)雜匹配,本文將介紹正則表達(dá)式的概念、語法和在編程中的應(yīng)用,并通過實(shí)例演示如何使用正則表達(dá)式進(jìn)行字符串匹配、替換和提取等操作2023-11-11
Java基于反射機(jī)制實(shí)現(xiàn)全部注解獲取的方法示例
這篇文章主要介紹了Java基于反射機(jī)制實(shí)現(xiàn)全部注解獲取的方法,結(jié)合實(shí)例形式分析了java反射機(jī)制獲取注解的具體實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2019-09-09

