8 种 Java - 内存溢出之四 - Metaspace
本文最后更新于:2024年7月25日 下午
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 中的类的数量强相关。下列代码就是最简单的例子:
1 |
|
在这个例子中,源代码循环遍历一个循环并在运行时生成类。所有这些生成的类定义在持续地消耗 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, 上述解决方案实际上不会改善任何事情,它只会推迟问题。
系列文章
- 8 种 Java 内存溢出之一:Java Heap Space
- 案例 1: 某财险承保系统内存泄漏问题
- 8 种 Java - 内存溢出之二 - GC overhead limit exceeded
- 案例 2: 某寿险公司核心系统 GC 开销超限问题分析
- 8 种 Java - 内存溢出之三 - Permgen space
- 案例 3: 某财险公司运行时的 Perm 区内存溢出分析
- 8 种 Java - 内存溢出之四 - Metaspace
- 8 种 Java - 内存溢出之五 - Unable to create new native thread
- 8 种 Java - 内存溢出六 - Out of swap space?
- 8 种 Java 内存溢出之七 - Requested array size exceeds VM limit
- 8 种 Java 内存溢出之八 - Kill process or sacrifice child