8 种 Java- 内存溢出之四 -Metaspace

本文最后更新于:2021年11月27日 下午

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
2
3
4
5
6
7
8
9
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, 上述解决方案实际上不会改善任何事情, 它只会推迟问题。

系列文章

  1. 8 种 Java 内存溢出之一:Java Heap Space
  2. 案例 1: 某财险承保系统内存泄漏问题
  3. 8 种 Java- 内存溢出之二 -GC overhead limit exceeded
  4. 案例 2: 某寿险公司核心系统 GC 开销超限问题分析
  5. 8 种 Java- 内存溢出之三 -Permgen space
  6. 案例 3: 某财险公司运行时的 Perm 区内存溢出分析
  7. 8 种 Java- 内存溢出之四 -Metaspace
  8. 8 种 Java- 内存溢出之五 -Unable to create new native thread
  9. 8 种 Java- 内存溢出六 -Out of swap space?
  10. 8 种 Java 内存溢出之七 -Requested array size exceeds VM limit
  11. 8 种 Java 内存溢出之八 -Kill process or sacrifice child

8 种 Java- 内存溢出之四 -Metaspace
https://ewhisper.cn/posts/46545/
作者
东风微鸣
发布于
2017年11月2日
许可协议