| 很多開發(fā)者都在其系統(tǒng)中見過“java.lang.OutOfMemoryError: PermGen space”這一問題。這往往是由類加載器相關(guān)的內(nèi)存泄漏以及新類加載器的創(chuàng)建導(dǎo)致的,通常出現(xiàn)于代碼熱部署時。相對于正式產(chǎn)品,該問題在開發(fā)機(jī)上出現(xiàn)的頻率更高,在產(chǎn)品中最常見的“問題”是默認(rèn)值太低了。常用的解決方法是將其設(shè)置為256MB或更高。 PermGen space簡單介紹PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域,說說為什么會內(nèi)存益出:這一部分用于存放Class和Meta的信息,Class在被 Load的時候被放入PermGen space區(qū)域,它和和存放Instance的Heap區(qū)域不同,所以如果你的APP會LOAD很多CLASS的話,就很可能出現(xiàn)PermGen space錯誤。這種錯誤常見在web服務(wù)器對JSP進(jìn)行pre compile的時候。 JVM 種類有很多,比如 Oralce-Sun Hotspot, Oralce JRockit, IBM J9, Taobao JVM(淘寶好樣的?。┑鹊取.?dāng)然武林盟主是Hotspot了,這個毫無爭議。需要注意的是,PermGen space是Oracle-Sun Hotspot才有,JRockit以及J9是沒有這個區(qū)域。 元空間(MetaSpace)一種新的內(nèi)存空間誕生JDK8 HotSpot JVM 將移除永久區(qū),使用本地內(nèi)存來存儲類元數(shù)據(jù)信息并稱之為:元空間(Metaspace);這與Oracle JRockit 和IBM JVM’s很相似,如下圖所示 這意味著不會再有java.lang.OutOfMemoryError: PermGen問題,也不再需要你進(jìn)行調(diào)優(yōu)及監(jiān)控內(nèi)存空間的使用……但請等等,這么說還為時過早。在默認(rèn)情況下,這些改變是透明的,接下來我們的展示將使你知道仍然要關(guān)注類元數(shù)據(jù)內(nèi)存的占用。請一定要牢記,這個新特性也不能神奇地消除類和類加載器導(dǎo)致的內(nèi)存泄漏。 java8中metaspace總結(jié)如下: PermGen 空間的狀況這部分內(nèi)存空間將全部移除。 JVM的參數(shù):PermSize 和 MaxPermSize 會被忽略并給出警告(如果在啟用時設(shè)置了這兩個參數(shù))。 Metaspace 內(nèi)存分配模型大部分類元數(shù)據(jù)都在本地內(nèi)存中分配。 用于描述類元數(shù)據(jù)的“klasses”已經(jīng)被移除。 Metaspace 容量默認(rèn)情況下,類元數(shù)據(jù)只受可用的本地內(nèi)存限制(容量取決于是32位或是64位操作系統(tǒng)的可用虛擬內(nèi)存大?。?。 新參數(shù)(MaxMetaspaceSize)用于限制本地內(nèi)存分配給類元數(shù)據(jù)的大小。如果沒有指定這個參數(shù),元空間會在運(yùn)行時根據(jù)需要動態(tài)調(diào)整。 Metaspace 垃圾回收對于僵死的類及類加載器的垃圾回收將在元數(shù)據(jù)使用達(dá)到“MaxMetaspaceSize”參數(shù)的設(shè)定值時進(jìn)行。 適時地監(jiān)控和調(diào)整元空間對于減小垃圾回收頻率和減少延時是很有必要的。持續(xù)的元空間垃圾回收說明,可能存在類、類加載器導(dǎo)致的內(nèi)存泄漏或是大小設(shè)置不合適。 Java 堆內(nèi)存的影響一些雜項數(shù)據(jù)已經(jīng)移到Java堆空間中。升級到JDK8之后,會發(fā)現(xiàn)Java堆 空間有所增長。 Metaspace 監(jiān)控元空間的使用情況可以從HotSpot1.8的詳細(xì)GC日志輸出中得到。 Jstat 和 JVisualVM兩個工具,在使用b75版本進(jìn)行測試時,已經(jīng)更新了,但是還是能看到老的PermGen空間的出現(xiàn)。 前面已經(jīng)從理論上充分說明,下面讓我們通過“泄漏”程序進(jìn)行新內(nèi)存空間的觀察…… PermGen vs. Metaspace 運(yùn)行時比較為了更好地理解Metaspace內(nèi)存空間的運(yùn)行時行為, 將進(jìn)行以下幾種場景的測試: 
 首先建立了一個模擬PermGen OOM的代碼 上面是一個簡單的ClassA,把他編譯成class字節(jié)碼放到D:/classes下面,測試代碼中用URLClassLoader來加載此類型上面類編譯成class 
 虛擬機(jī)器參數(shù)設(shè)置如下:-verbose -verbose:gc 設(shè)置-verbose參數(shù)是為了獲取類型加載和卸載的信息 設(shè)置-verbose:gc是為了獲取垃圾收集的相關(guān)信息 JDK 1.7 @64-bit – PermGen 耗盡測試Java1.7的PermGen默認(rèn)空間為85 MB(或者可以通過-XX:MaxPermSize=XXXm指定) 可以從上面的JVisualVM的截圖看出:當(dāng)加載超過6萬個類之后,PermGen被耗盡。我們也能通過程序和GC的輸出觀察耗盡的過程。 程序輸出(摘取了部分) 
 JDK 1.8 @64-bit – Metaspace大小動態(tài)調(diào)整測試Java的Metaspace空間:不受限制 (默認(rèn)) 從上面的截圖可以看到,JVM Metaspace進(jìn)行了動態(tài)擴(kuò)展,本地內(nèi)存的使用由20MB增長到646MB,以滿足程序中不斷增長的類數(shù)據(jù)內(nèi)存占用需求。我們也能觀察到JVM的垃圾回收事件—試圖銷毀僵死的類或類加載器對象。但是,由于我們程序的泄漏,JVM別無選擇只能動態(tài)擴(kuò)展Metaspace內(nèi)存空間。程序加載超過10萬個類,而沒有出現(xiàn)OOM事件。 JDK 1.8 @64-bit – Metaspace 受限測試Java的Metaspace空間:128MB(-XX:MaxMetaspaceSize=128m) 可以從上面的JVisualVM的截圖看出:當(dāng)加載超過2萬個類之后,Metaspace被耗盡;與JDK1.7運(yùn)行時非常相似。我們也能通過程序和GC的輸出觀察耗盡的過程。另一個有趣的現(xiàn)象是,保留的原生內(nèi)存占用量是設(shè)定的最大大小兩倍之多。這可能表明,如果可能的話,可微調(diào)元空間容量大小策略,來避免本地內(nèi)存的浪費(fèi)。 從Java程序的輸出中看到如下異常。 在設(shè)置了MaxMetaspaceSize的情況下,該空間的內(nèi)存仍然會耗盡,進(jìn)而引發(fā)“java.lang.OutOfMemoryError: Metadata space”錯誤。因為類加載器的泄漏仍然存在,而通常Java又不希望無限制地消耗本機(jī)內(nèi)存,因此設(shè)置一個類似于MaxPermSize的限制看起來也是合理的。 總結(jié)
 | 
|  |