英文原稿:http://vdisk.weibo.com/s/vxGdGZEZTEjkhtml
中文整理稿:http://it.deepinmind.com/gc/2014/05/14/metaspace-in-java-8.htmljava
其實上面的整理稿,是針對這篇英文文稿的翻譯兼整理,可是感受有點亂,有點強迫症,因此乾脆本身來翻譯。關於vm相關性能數據的監控部分略掉了,此稿件主要是介紹perm gen的設計與移除,以及移除perm gen以後新的內存模型,因此後面關於內存的調優還有性能的監測都過於簡單,若是想了解,能夠去看周志明先生的《深刻理解Java虛擬機》,裏面對jstat等工具備更多的介紹。web
翻譯:數組
Oracle HotSpot JVM中的perm gen保存了HotSpot用於描述Java對象(Java objects)的元數據(metadata)。perm gen在Full GC中會和Java堆(Java Heap)一塊兒經歷垃圾回收。perm gen在JDK8中的HotSpot中,已經被移除了。本節簡單的描述了perm gen及移除它的動機。還討論了移除perm gen可能對Java程序的執行形成的多方面的影響。數據結構
議題併發
- 什麼是perm gen
- 如今的元數據在哪兒
- 壓縮的類指針
- 新的調節參數
- Memory Pool MXBeans
什麼是perm gen函數
- 全名permanent generation
- 是Java Heap中用於存儲虛擬機類的元數據的區域
- Java類在HotSpot中的內在表現
- 類的結構信息,字段,名字
- 方法的彙編信息和字節碼
- Vtables(虛函數表)
- 常量池和符號解析
含有perm gen的vm內存分佈示意圖:工具
![](http://static.javashuo.com/static/loading.gif)
PermGen 大小oop
- 1.受限於參數MaxPermSize(默認是64M-85M)
- 2.須要Java Heap中連續的存儲空間:若是分配的堆空間不連續,那麼從old gen和perm gen定位指向新對象的引用,代價就會更大,並且也會更復雜。(card table remembered set)
- 3.一旦耗盡,就會拋異常
- 清除引用來觸發類卸載
- 配置更大的MaxPermSize
- 4.perm gen的大小依賴於類的數量,方法的大小,以及常量池的大小
爲何要移除PermGen性能
- 啓動的時候就要指定大小,難以調優:MaxPermSize要設多大?
- HotpSpot內部類型是Java對象,可能會在Full GC中移動,對應用程序來講,不透明,而且非強類型,很難調試,須要用於存儲元數據的空間
- 簡化Full GC
- 但願能併發的回收類型數據空間,而不是在GC停頓期間
- 使受限於permgen的將來的改進成爲可能
元數據去哪兒了?
![](http://static.javashuo.com/static/loading.gif)
得益於Java語言特性:類及相關元數據的生命週期和累加載器相匹配
每個加載器的存儲空間
- 僅線性分配
- 沒有單獨的回收(除非RedefineClasses和類加載失敗)
- 沒有GC掃描和整理
- 不須要從新安置元數據空間對象
- 當GC發現類加載器死亡,則整個空間一塊兒回收
這些存儲空間的集體稱爲Metaspace
Metaspace 分配
- 爲元數據分配多個映射虛擬內存空間
- 爲每一個類加載器分配塊狀空間列表
- 塊的大小依賴於類加載器的大小
- 爲sun/reflect/DelegatingClassLoader, JSR292匿名類的加載器分配較小的塊
- 歸還內存塊,以釋放塊狀列表
- 當被清空的時候,歸還虛擬內存空間
- 設計諸多策略來減小碎片
以下圖:
![](http://static.javashuo.com/static/loading.gif)
(圖中CL爲類加載器,Boot CL爲啓動類加載器,vs1,vs2,vs3爲虛擬內存空間,黃色的塊爲元數據塊)
Java對象內存分配
![](http://static.javashuo.com/static/loading.gif)
堆上對象有指向Metaspace中本身類信息的指針—>_klass
在64位平臺上,爲了壓縮JVM對象中的類指針,引入了「壓縮類指針空間」(對象中的_klass變爲4字節,指向壓縮類空間中的數據,該數據再指向Metaspace中的類信息)
![](http://static.javashuo.com/static/loading.gif)
壓縮指針(壓縮類指針,壓縮對象指針)
- 默認是爲了64位平臺
- 使用-XX:+UseCompressedOops 來壓縮對象指針
- oops是指普通對象指針
- Java堆中對象的對象指針被壓縮到32bit
- 使用堆基地址(若是在低26G內存空間中,爲0)即,指針的偏移量針對於堆的基地址
- 使用-XX:+UseCompressedClassPointers 壓縮類指針
- 對象的類指針(_klass)被壓縮至32bit
- 使用類指針壓縮空間的基地址
Metaspace與類壓縮空間的對比
- 類壓縮空間只包含像接口指針(InstanceKlass),數組指針(ArrayKlass)等元數據
- 僅當UseCompressedClassPointers爲true的時候會這樣
- 爲了性能問題,這裏還保存了Java 虛方法表
- 咱們仍然在減小這個元數據類型
- 而Metaspace包含了元數據的其餘全部的數據,這可能比較大,如方法,字節碼,常量池
GC性能的提高
- 在Full GC,元數據到元數據的指針不須要掃描
- 一大堆元數據掃描的複雜代碼(尤爲在CMS)都被刪除了
- 元數據空間包含較少的指向Java Heap的指針
- 指向java/lang/Class對象的指針存儲在類元數據中
- 數據類元數據中,有指向其成員類Class對象的指針
- 咱們要移除這些
- 元數據再也不有整理消耗
- 減小了根掃描(不掃描VM已加載類的字典和其餘內部的哈希表)
- Full GC的時間將會縮短(不一樣狀況下可能會有所不一樣)
- 在G1中能夠在併發標記以後進行類卸載
- 能夠調優
調優標誌
MaxMetaspaceSize
- -XX:MaxMetaspaceSize={unlimited}
- Metaspace受機器內存的限制
- 在虛擬內存切換頻繁和本地內存分配失敗以前,限制類元數據使用的內存
-
- 若是類加載器內存可能泄漏
- 若是在32位平臺上,地址空間可能耗盡
- MaxMetaspaceSize是指Metaspace和壓縮類指針空間的總和
MetaspaceSize
- 初始大小爲21mb,超過這個值,將進行Full GC來回收類
- GC的相關操做用於檢測已死的類加載器,和未加載的類
- 若是在啓動階段發生了過多的GC,則須要設置一個高點的限制
- 可使用與PermSize相同的值,來延緩初始GC
- 在下次Metaspace GC以前,High water mark會隨着接下來合理數量的頭空間的收集而增高
CompressedClassSpaceSize
- 當使用了-XX:+UseCompressedClassPointers 纔有效
- -XX:CompressedClassSpaceSize=1G
- 由於這個空間只能在啓動的時候設置,因此一開始就設置大一點
- 不使用的時候,不會分配
- 將來的工做,是要使這塊空間可增加
- 不須要連續,只須要從基地址可達
- 更傾向於將更多的類元數據移到Metaspace中
- 將來可能會以性能爲主導
- PredictedLoadedClassCount (該標誌目前處於試驗性):用於設置其餘JVM內部數據結構的大小,像已加載類的字典
- 開發者可能會發現對於CompressedClassSpaceSize和UseCompressedClassPointers的不一樣命名。但在JDK-8015107中已經設定,對metaspace的概念,須要使用統一的命名方式
能夠經過-XX:+PrintGCDetails and -XX:+PrintHeapAtGC來查看metaspace的使用狀況
PermGen內存池之前在內存管理中屬於Heap內存池的列表中,如今移除了
總結:
- HotSpot 元數據如今存在元空間中
- 有調優標誌,雖然並非必需要使用的
- 這種改變使得其餘優化與特性成爲可能
- 共享類數據
- 新生代優化,G1進行類卸載
- 元數據減小
譯者有話說:
表示有些地方,我沒看懂,不過總的仍是有收穫的。並且,我感受是指導性文檔,摳字眼就不必了。最近一直在看JVM的相關內容,總結一句話,發展時間過久,各類詞彙亂用,致使哪哪兒都模棱兩可。
好比,如今從一些較官方的文檔來看,Java Heap和咱們常說的java堆是不一樣的,咱們常說的java堆只包含新生代和老年代,是用來存儲對象實例數據的,可是Java Heap是包括perm gen在內的GC做用的整個堆空間。有人說,這有神馬影響嗎?沒影響的時候沒影響,有影響的時候,真是搞腦子。
簡單點歸納
過去類的元數據存儲在perm gen,做爲Java Heap的一部分,使用instanceKlass,instanceKlassKlass,klassKlass(詳見R大關於perm gen數據的追蹤博客),接受GC傳統意義上的掃描。弊端:內存受限,容易溢出;空間回收性能低等。如今的元數據存在元空間中。元空間由多個虛擬內存空間構成,每一個虛擬內存空間又被劃分爲不一樣大小的塊。以類加載器爲源頭,隨着類的不斷加載,不斷從虛擬內存空間中分配塊空間,從而使每一個類加載器對應了一個塊列表。當GC檢測到某個類加載器已死,就總體釋放整個列表,回收內存。
至於壓縮類空間,出於多個緣由考慮,主要有兩個。第一,在64位平臺,指針膨脹致使內存浪費,使用壓縮類指針,壓縮類指針空間基地址的概念,繼續使用32位指針尋址;第二,這樣一個空間還能夠存儲部分元數據,以提升相應性能。