JVM載入類的時候,需要記錄類的後設資料,這些資料會儲存在一個單獨的記憶體區域內,在Java 7裡,這個空間被稱為永久代(Permgen),在Java 8裡,使用元空間(Metaspace)代替了永久代。永久代和元空間儲存的資料並不完全一樣,永久代中還儲存另一些與類的後設資料無關的雜項。
如我們之前的一篇文章016:字串物件在JVM中是如何存放的中說的,在Java 7裡將字串常量從永久代移動到了堆區域,但是永久代並沒有完全改造完成。直到Java 8,永久代的改造才算完全搞定,在元空間中儲存的資料比永久代中純粹很多,就是類的後設資料,這些資訊只對編譯期或JVM的執行時有用。
理論學習
使用Java 8以後,關於元空間的JVM引數有兩個:-XX:MetaspaceSize=N
和-XX:MaxMetaspaceSize=N
,對於64位JVM來說,元空間的預設初始大小是20.75MB,預設的元空間的最大值是無限。MaxMetaspaceSize用於設定metaspace區域的最大值,這個值可以通過mxbean中的MemoryPoolBean獲取到,如果這個引數沒有設定,那麼就是通過mxbean拿到的最大值是-1,表示無窮大。
由於調整元空間的大小需要Full GC,這是非常昂貴的操作,如果應用在啟動的時候發生大量Full GC,通常都是由於永久代或元空間發生了大小調整,基於這種情況,一般建議在JVM引數中將MetaspaceSize和MaxMetaspaceSize設定成一樣的值,並設定得比初始值要大,對於8G實體記憶體的機器來說,一般我會將這兩個值都設定為256M(PS:讀者可以根據自己的實際情況再調整)。
原始碼分析
MetaspaceSize表示metaspace首次使用不夠而觸發FGC的閾值,只對觸發起作用,原因是:垃圾蒐集器內部是根據變數_capacity_until_GC
來判斷metaspace區域是否達到閾值的,初始化程式碼如下所示:
void MetaspaceGC::initialize() {
// Set the high-water mark to MaxMetapaceSize during VM initializaton since
// we can't do a GC during initialization.
_capacity_until_GC = MaxMetaspaceSize;
}複製程式碼
GC收集器會在發生對metaspace的回收會,會計算新的capacityuntil_GC值,以後發生FGC就跟MetaspaceSize沒有關係了。
如果不設定MetaspaceSize,則預設的capacityuntil_GC為20M左右,具體程式碼如下:![螢幕快照 2018-10-16 下午6.46.27.png](https://user-gold-cdn.xitu.io/2019/10/4/16d97024b10550ee?w=1240&h=301&f=png&s=195510)***本號專注於後端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這裡有所收穫。