JVM加載類的時候,須要記錄類的元數據,這些數據會保存在一個單獨的內存區域內,在Java 7裏,這個空間被稱爲永久代(Permgen),在Java 8裏,使用元空間(Metaspace)代替了永久代。永久代和元空間保存的數據並不徹底同樣,永久代中還保存另外一些與類的元數據無關的雜項。java
如咱們以前的一篇文章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面試題、我的成長和自我管理等主題,爲讀者提供一線開發者的工做和成長經驗,期待你能在這裏有所收穫。spa