永久代和元空間的變化

chao430發表於2019-03-18

       隨著JDK8的到來,JVM不再有PermGen。但類的後設資料資訊(metadata)還在,只不過不再是儲存在連續的堆空間上,而是移動到叫做“Metaspace”的本地記憶體(Native memory)中。

       其實,移除永久代的工作從JDK1.7就開始了。JDK1.7中,儲存在永久代的部分資料就已經轉移到了Java Heap或者是 Native Heap。但永久代仍存在於JDK1.7中,並沒完全移除,譬如符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap(上一篇文章Java-String.intern的深入研究 有專門提到這個變化);類的靜態變數(class statics)轉移到了java heap。

        類的後設資料資訊轉移到Metaspace的原因是PermGen很難調整。PermGen中類的後設資料資訊在每次FullGC的時候可能會被收集,但成績很難令人滿意。而且應該為PermGen分配多大的空間很難確定,因為PermSize的大小依賴於很多因素,比如JVM載入的class的總數,常量池的大小,方法的大小等。

       此外,在HotSpot中的每個垃圾收集器需要專門的程式碼來處理儲存在PermGen中的類的後設資料資訊。從PermGen分離類的後設資料資訊到Metaspace,由於Metaspace的分配具有和Java Heap相同的地址空間,因此Metaspace和Java Heap可以無縫的管理,而且簡化了FullGC的過程,以至將來可以並行的對後設資料資訊進行垃圾收集,而沒有GC暫停。

 

深入理解元空間(Metaspace)

 元空間的記憶體大小

元空間是方法區的在HotSpot jvm 中的實現,方法區主要用於儲存類的資訊、常量池、方法資料、方法程式碼等。方法區邏輯上屬於堆的一部分,但是為了與堆進行區分,通常又叫“非堆”。

元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用本地記憶體。,理論上取決於32位/64位系統可虛擬的記憶體大小。可見也不是無限制的,需要配置引數。

 

 

為什麼廢棄永久代 ?

       絕大部分 Java 程式設計師應該都見過 "java.lang.OutOfMemoryError: PermGen space "這個異常。這裡的 “PermGen space”其實指的就是方法區。不過方法區和“PermGen space”又有著本質的區別。前者是 JVM 的規範,而後者則是 JVM 規範的一種實現,並且只有 HotSpot 才有 “PermGen space”,而對於其他型別的虛擬機器,如 JRockit(Oracle)、J9(IBM) 並沒有“PermGen space”。由於方法區主要儲存類的相關資訊,所以對於動態生成類的情況比較容易出現永久代的記憶體溢位。最典型的場景就是,在 jsp 頁面比較多的情況,容易出現永久代記憶體溢位。

    1. 官方解釋

參照JEP122:http://openjdk.java.net/jeps/122,原文擷取:

Motivation

This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need to configure the permanent generation (since JRockit does not have a permanent generation) and are accustomed to not configuring the permanent generation.

 即:移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力,因為JRockit沒有永久代,不需要配置永久代。

       

   2.現實中易出問題

        a、字串存在永久代中,容易出現效能問題和記憶體溢位。

  b、類及方法的資訊等比較難確定其大小,因此對於永久代的大小指定比較困難,太小容易出現永久代溢位,太大則容易導致老年代溢位。

  c、永久代會為 GC 帶來不必要的複雜度,並且回收效率偏低。

 

元空間的調優 
        元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制,但可以通過以下引數來指定元空間的大小:


       -XX:MaxMetaspaceSize引數可以設定元空間的最大值,預設是沒有上限的,也就是說你的系統記憶體上限是多少它就是多少。

          -XX:MetaspaceSize選項指定的是元空間的初始大小,如果沒有指定的話,元空間會根據應用程式執行時的需要動態地調整大小,達到該值就會觸發垃圾收集進行型別解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就適當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,適當提高該值。


MaxMetaspaceSize的調優 

- -XX:MaxMetaspaceSize={unlimited} 
- 元空間的大小受限於你機器的記憶體 
- 限制類的後設資料使用的記憶體大小,以免出現虛擬記憶體切換以及本地記憶體分配失敗。如果懷疑有類載入器出現洩露,應當使用這個引數;32位機器上,如果地址空間可能會被耗盡,也應當設定這個引數。 
- 元空間的初始大小是21M——這是GC的初始的高水位線,超過這個大小會進行Full GC來進行類的回收。
- 如果啟動後GC過於頻繁,請將該值設定得大一些 
- 可以設定成和持久代一樣的大小,以便推遲GC的執行時間 

除了上面兩個指定大小的選項以外,還有兩個與 GC 相關的屬性:  

     -XX:MinMetaspaceFreeRatio,在GC之後,最小的Metaspace剩餘空間容量的百分比,減少為分配空間所導致的垃圾收集   -XX:MaxMetaspaceFreeRatio,在GC之後,最大的Metaspace剩餘空間容量的百分比,減少為釋放空間所導致的垃圾收集

參考文章:https://www.cnblogs.com/yulei126/p/6777323.html

          https://blog.csdn.net/zhushuai1221/article/details/52122880 

相關文章