4. Metaspace

4.1 java.lang.OutOfMemoryError: Metaspace 概述

Java應用只允許使用有限的內存. 你的應用可以用的準確的內存大小在啓動的時候指定. 展開來說, Java內存被分成不同的區域, 具體如下圖:

所有的這些區域, 包括元空間(metaspace)區域, 可以在JVM啓動的時候指定. 如果你沒有指定這些的大小, 平臺相關的默認配置會被應用.

java.lang.OutOfMemoryError: Metaspace 消息表明Metaspace區內存耗盡.

4.2 原因

如果你不是Java領域的新手, 你可能會熟悉Java內存管理的另一個叫做: PermGen的概念. 從Java 8開始, Java的內存模型發生明顯改變. 引入一個叫做Metaspace的新的內存區域, Permgen被移除. 這個變更是基於多種原因的, 包括但不限於:

  • PermGen需要的內存大小難以預測. 它導致有可能由於內存不足觸發java.lang.OutOfMemoryError: Permgen錯誤或預留過多導致浪費資源.
  • GC性能的提升, 在沒有GC暫停和元數據的特定迭代器的情況下啓用併發類數據分配.
  • 支持進一步優化,如G1併發類卸載。

所以, 如果你熟悉PermGen, 那麼你需要知道的就是 – Java 8之前版本在PermGen裏存在的一切東西(組成類的類的名字和字段, 方法的字節碼, 常量池信息, 對象數組和類型數組, 以及實時編譯優化) – 限制都在Metaspace裏.

如你所見, Metaspace 大小需求取決於加載的類的數量和這些類聲明的大小. 所以, 很明顯java.lang.OutOfMemoryError: Metaspace的主要原因是: 要不太多類, 要不太大的類被加載到Metaspace中.

4.3 案例

就如我們在之前解釋的那樣, Metaspace使用率與加載到JVM中的類的數量強相關. 下列代碼就是最簡單的例子:

public class Metaspace {
    static javassist.ClassPool cp = javassist.ClassPool.getDefault();

    public static void main(String[] args throws Exception {
        for (int i=0; ;i++) {
            Class c = cp.makeClass("eu.plumbr.demo.Generated" + i).toClass();
        }
    }
}

在這個例子中, 源代碼循環遍歷一個循環並在運行時生成類. 所有這些生成的類定義在持續地消耗Metaspace. javassist庫對類生成的複雜性進行了處理.

代碼會持續生成新的類, 並加載他們的定義到Metaspace中直到空間被完全佔滿, java.lang.OutOfMemoryError: Metaspace拋出. 當在Mac OS X, Java 1.8.0_05上使用-XX:MaxMetaspaceSize=64m大概加載70,000個類會死掉.

4.4 解決方案

第一個解決因爲Metaspace內存溢出的方案很明顯. 如果應用耗盡了Metaspace的內存, 你應該增加Metaspace的大小. 調整應用運行配置, 調整下列參數:
-XX:MaxMetaspaceSize=512m

上述配置案例告訴JVM, Metaspace允許在拋出OutOfMemoryError的錯誤之前增長到512MB.

另一個解決方案乍一看更容易. 你可以通過刪除這個參數移除Metaspace的限制. 但是需要注意的是, 你這麼做, 可能會導致swap的大量消耗和/或導致本機物理內存分配失敗.

通過使用上述建議的 “快速修復”, 你只會通過隱藏 java.lang.OutOfMemoryError: Metaspace掩蓋症狀, 而不是從根本上解決問題.

如果應用程序內存泄漏或是加載的不合理的東西到Metaspace, 上述解決方案實際上不會改善任何事情, 它只會推遲問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章