《JVM故障診斷指南》之4 - Java 8:從持久代到metaspace
原文鏈接 原文作者:James D Bloom 翻譯:梅小西(904516706)
Java 8介紹了一些新語言以及運行時新特點。其中一個特點便是完全移除了持久代(PermGen),自從Oracle公司發布了JDK1.7后就已經宣布了這個 決定。還有比如內部字符串,從JDK1.7開始就從持久代移除了,JDK8的發布徹底廢除了它。在這個部分,我們會討論持久代的繼任 者:Metaspace。
當執行一個Java程序并出現了“泄露”類元數據對象時我們會比較HotSpot 1.7和HotSpot 1.8的運行時行為的不同點。
一旦Java 8 正式發布,關于Metaspace的最終的參考規范,調優標記以及文檔應該就能使用了。
Metaspace:一個新的內存空間誕生了
JDK8 HotSpot JVM現在使用了本地內存來存儲類元數據,被稱為Metaspace,和Oracle JRockit以及IBM JVM類似。
好消息是它意味著java.lang.OutOfMemoryError:PermGen space問題會越來越少,也不再需要你去調整和監控內存空間。然而這種變化默認是可不見的,接下來我們給你展示的,是你仍然需要關注類元數據內存占用。 請記住,這些新特點并不會很神奇的消除類和類加載器的內存泄露。你需要使用不同的方法和學習新的命名約定來找出問題的根源。
總結:
? 持久代場景:
? 這塊內存區域被完全移除。
? PermSize和MaxPermSize JVM 參數會被忽略,并且在啟動的時候會給出警告信息。
? Metaspace 內存分配模型
? 對于類元數據的大多數內存分配都不會發生在本地內存。
? 被用于描述類元數據的類對象被移除。
?Metaspace 容量
? 默認的,類元數據分配限制于可用的本地內存 (容量大小依賴于你用32位jvm或者64位jvm的操作系統可用虛擬內存)。
? 新的標記已經可以使用 (MaxMetaspaceSize),它允許你限制用于類元數據的本地內存大小。如果你沒有指定這個標記,Metaspace會根據運行時應用程序的需求來動態的控制大小。
?Metaspace 垃圾收集
? 一旦類元數據的使用量達到了“MaxMetaspaceSize”指定的值,對于無用的類和類加載器,垃圾收集此時會觸發。
? 為了控制這種垃圾收集的頻率和延遲,合適的監控和調整Metaspace非常有必要。過于頻繁的Metaspace垃圾收集是類和類加載器發生內存泄露的征兆,同時也說明你的應用程序內存大小不合適,需要調整。
?Java 堆空間影響
一些雜項數據被移到了Java堆空間。這意味著當你更新到JDK8后會觀察到Java堆空間的增長。
?Metaspace 監控
? Metaspace 的使用可以通過HotSpot 1.8的詳細的GC日志輸出觀察到。
? 在基于b75上測試的時候Jstat 和 JVisualVM 還沒有更新,舊的持久代空間引用依然存在。
足夠的理論知識就介紹到這,讓我們在行動中通過會發生泄露的Java程序來看看新的內存空間…
持久代 vs. Metaspace運行時比較
為了能更好的理解新的metaspace內存空間在運行時的行為,我們創建了一個會發生元數據泄露的java程序。你可以從這里下載(準備梯子)。
下面的場景將會被測試:
? 使用JDK1.7運行這個Java程序,目的是為了監控和消耗設置好的128M持久代空間。
? 使用JDK1.8(b75)運行這個Java程序,目的是為了監控Metaspace內存空間的動態增長和垃圾收集。
? 使用JDK1.8(b75)運行這個Java程序,設置MaxMetaspaceSize為128M,目的是為了模擬Metaspace空間的消耗。
JDK 1.7 @64-bit – 持久代消耗
? 一個包含5萬個配置好的迭代的程序
? 1024M的java堆
? 128M java持久代(-XX:MaxPermSize=128m)
從JVisualVM里可以看到,持久代的消耗在加載了超過3萬個類之后幾乎達到了臨界。我們也可以從Java程序和GC輸出中看到這種消耗。
現在讓我們用HotSpot JDK 1.8 來執行這個程序。
JDK 1.8 @64-bit – Metaspace 動態大小
? 一個包含5萬個配置好的迭代的程序
? 1024M的堆
? Java Metaspace空間:無限(默認)
從詳細的GC輸出可以看到,JVM的metaspace的確動態的把本地內存從20M擴展到了320M,目的是為了適應增長的Java程序中類元數 據的內存占用。我們也可以觀察到JVM會嘗試進行垃圾收集的事件,目的是為了消滅無用的類和類加載器對象。自從我們的Java程序開始泄露內存,JVM沒 有選擇,只能動態擴展Metaspace內存空間。
這個程序可以運行5萬次迭代而不會發生OOM事件,并且加載了超過5萬個類。
讓我們轉移到我們最后一次測試場景:
JDK 1.8 @64-bit – Metaspace 消耗
? 一個包含5萬個配置好的迭代的程序
? 1024M的堆
? Java Metaspace空間:128 MB (-XX:MaxMetaspaceSize=128m)
從JVisualVM里可以看到,在加載了超過3萬個類后,Metaspace消耗達到了臨界,和用JDK1.7運行的結果類似。我們可以從程序和 GC輸出中看到這個結果。另一個有意思的觀察結果是本地內存占用是指定最大值的2倍。這或許可以說明,一種好的調整metaspace擴容的策略有可能避 免本地內存的浪費。
和用JDK1.7運行一樣,我們指定了metaspace最大容量為128M,但它在我們程序里并不能完成5萬次的迭代。新的OOM會被拋出。上面的OOM事件是在內存分配失敗后由JVM從metaspace里拋出的。.
關于metaspace的總結
目前觀察到的結果完全說明了合適的監控和調優是非常必要的,目的是為了盡量避免類似我們最后一種測試場景中過多的metaspace GC或者OOM觸發的問題。
譯者介紹:
梅小西
Java工程師,關注JVM,并發編程,喜歡研究Python,Scala,Golang等。
譯者相關譯文:
JVM內部原理
《JVM故障診斷指南》之1 ——JVM概覽與介紹
《JVM故障診斷指南》之2 ——調整合適的Java堆大小的技巧
《JVM故障診斷指南》之3 ——Java 線程: JVM持有內存的分析
《JVM故障診斷指南》之4 ——Java 8:從持久代到metaspace
原創文章,轉載請注明: 轉載自并發編程網 – ifeve.com