bigywater.tech
Java · 163

Metaspace:类元数据为什么会把进程撑爆

Metaspace 里存什么、为什么会被撑爆、什么时候被回收?这篇用 BufferPool/MemoryPool 的数字和 hidden class 做的小实验量出来(类卸载的细节、元空间碎片在后面的篇目)。

7962 个类
MaxMetaspaceSize=24m 时撑爆 Metaspace 前能定义的类数
10704 → 911 KB
5000 个不可达类的 Metaspace,GC 前后
无上限
默认的 MaxMetaspaceSize

Metaspace 与堆

实测 · 本机执行输出
  起始:Metaspace 已用 710 KB,已加载类 826,堆已用 3104 KB
  再定义 5000 个相同内容的类(都保留引用):Metaspace 已用 10673 KB(涨了约 9965 KB),已加载类 5855,堆已用 10272 KB(堆也涨了约 7 MB,具体构成没有细查)
  Metaspace 已提交 16192 KB,Compressed Class Space 已用 2786 KB

撑爆

实测 · 本机执行输出
  保留引用地不断定义类,成功定义 7962 个之后 → OutOfMemoryError: Metaspace

类卸载

实测 · 本机执行输出
  起始 Metaspace 已用 714 KB,已卸载类 0
  定义 5000 个类但不保留引用:Metaspace 已用 10704 KB(9989 KB 增长),已卸载类 0
  System.gc() 之后:Metaspace 已用 911 KB,已卸载类 5000,已加载类 861

默认参数

实测 · 本机执行输出
  MetaspaceSize = 22020096(即 21 MB)
  MaxMetaspaceSize = 18446744073709551615(即无上限)
  CompressedClassSpaceSize = 1073741824(即 1024 MB)

分步演示

Metaspace
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,用 MethodHandles.Lookup.defineHiddenClass 重复定义同一个类
    • 没有验证:真实的 ClassLoader 泄漏场景(如热部署)、Metaspace 碎片(见后面的篇目)、JDK 17 与 27 的参数默认值、容器内存限制下 Metaspace 无上限的实际后果、Compressed Class Space 单独耗尽的 OOM;每个类 2 KB 是这个小类的数字
    京ICP备2026067502号-1京公网安备11010502063487号