前言
首先需要明确的是,以下我們讨論的HotSpot虛拟機,其他類型的虛拟機,例如JRockit與J9等,壓根就沒有永久代的概念。是以,下面所說的“虛拟機”都是HotSpot版本的。
要想了解這種變化的原因,需要先了解方法區、永久代與元空間的概念與之間的關系。
方法區與永久代,元空間之間的關系
方法區是一種規範,不同的虛拟機廠商可以基于規範做出不同的實作,永久代和元空間就是出于不同jdk版本的實作。
說白了,方法區就像是一個接口,永久代與元空間分别是兩個不同的實作類而已。隻不過永久代是這個接口最初的實作類,後來這個接口一直進行變更,直到最後徹底廢棄這個實作類,由新實作類——元空間進行替代。
方法區
借用《深入了解Java虛拟機——JVM進階特性與最佳實踐》中介紹方法區的段落
方法區和堆一樣,是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯後的代碼等資料。
Java7及以前版本的永久代的結構
在Java7及以前的版本,是存在永久代的。在Java7版本時,永久代已經發生了悄悄的變化。等到Java8時,徹底廢棄了永久代,由元空間替換。
永久代與堆的構造如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SM4ADNxQTZkhTOzYjM2ITNzYzXzAjMxcTMxIzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
(關于堆中的Eden、from與to區域,可以先參考我的另外一篇文章【JVM】說說java中的堆區)
從上圖中可以看到,永久代與堆中的老年代是連續的,這裡的連續指的是實體位址連續,永久代本身并不在堆中。是以,老年代與永久代其中一個滿了,都會觸發Full GC。
我們可以使用以下的指令,來顯示指定永久代的大小:
- -XX:PremSize:設定永久代的初始大小
- -XX:MaxPermSize: 設定永久代的最大值
由于方法區主要存儲類的相關資訊,是以對于動态生成類的情況比較容易出現永久代的記憶體溢出。最典型的場景就是,在 jsp 頁面比較多的情況,容易出現永久代記憶體溢出,會報出"java.lang.OutOfMemoryError: PermGen space "異常。
Java7時,永久代的變化
在Java7時,仍然有永久代,永久代也與堆中的老年代連續,但永久代中存儲的部分資料已經開始轉移到Java Heap或Native Memory中了,比如:
- 符号引用(Symbols)轉移到了Native Memory
- 字元串常量池(interned strings)轉移到了Java Heap
- 類的靜态變量(class statics)轉移到了Java Heap
現在分别在Java6、7、8環境中循環調用String.intern()方法(關于此方法,可以先移步到我的另外一篇文章中【JAVA】String源碼淺談,裡面有對此方法的介紹與實驗),那麼分别會報出以下區域的記憶體溢出異常
- Java6時,記憶體溢出區域為永久代。因為在Java6及之前,字元串常量池在永久代中
- Java7時,記憶體溢出區域為堆中。因為在Java7時,字元串常量池被移到堆中了。
- Java8時,記憶體溢出區域仍然為堆中,不過此時已經沒有永久代了。
Java8開始,永久代就已經消失了,由元空間取而代之。
元空間
元空間(Metaspace),不再與堆連續,而是直接存在于本地記憶體中,也就是機器的記憶體。理論上機器記憶體有多大,元空間的野心就有多大。但可以通過以下的參數來設定元空間的大小:
- -XX:MetaspaceSize,初始空間大小,達到該值就會觸發垃圾收集進行類型解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就适當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,适當提高該值。
- -XX:MaxMetaspaceSize,最大空間,預設是沒有限制的。
除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:
- -XX:MinMetaspaceFreeRatio,在GC之後,最小的Metaspace剩餘空間容量的百分比,減少為配置設定空間所導緻的垃圾收集
- -XX:MaxMetaspaceFreeRatio,在GC之後,最大的Metaspace剩餘空間容量的百分比,減少為釋放空間所導緻的垃圾收集
在使用-XX:MaxMetaspaceSize顯示指定元空間的大小為一個比較小的值,接着在循環中動态加載過多的類,那麼會報出"java.lang.OutOfMemoryError: Metaspace"異常。