方法区 / 元空间:JDK 1.7 到 JDK 1.8 到底变了什么?

如果你只想记一个结论:

  • JDK 1.7 :有永久代(PermGen
  • JDK 1.8 :移除永久代,用元空间(Metaspace,走本地内存)

但只记结论不够,因为你线上会遇到:

  • OutOfMemoryError: PermGen space
  • OutOfMemoryError: Metaspace
  • 动态代理/反射/生成类太多导致内存异常

这篇把"方法区是什么、放什么、为什么改、怎么排"讲透。


1. 方法区是什么:放"类本身的信息"的地方

方法区是 JVM 规范里的一个概念,主要存:

  • 类元数据:类结构、字段/方法信息、访问标志等
  • 运行时常量池:字符串常量、符号引用等
  • 静态变量相关信息(具体实现上会有差异)
  • JIT 相关的一些元数据(不同实现有所不同)

一句话:

  • 堆里放对象
  • 方法区(元空间)放类

2. JDK 1.7:永久代(PermGen)

2.1 它为什么容易 OOM

永久代属于 HotSpot 的实现细节(不是 JVM 规范硬规定),它的问题在于:

  • 空间大小受 JVM 参数限制(配置不当很容易顶满)
  • 类卸载条件苛刻(尤其在容器里反复热部署)

典型场景:

  • Tomcat 反复热部署,类加载器泄露
  • 大量动态生成类(CGLIB、ASM、Javassist)
  • 运行时常量池/字符串占用膨胀

2.2 常见参数(JDK 1.7)

  • -XX:PermSize
  • -XX:MaxPermSize

3. JDK 1.8:元空间(Metaspace)

3.1 为什么要改

把类元数据从永久代迁到元空间,核心动机:

  • 永久代太容易 OOM,且难以调优
  • 类元数据更适合放到本地内存,避免和 Java 堆争抢

3.2 元空间还会 OOM 吗

会。

  • 元空间用的是本地内存,不等于无限
  • 如果你不设上限,可能把机器内存吃光
  • 如果你设了上限,达到上限一样会 OutOfMemoryError: Metaspace

3.3 常见参数(JDK 1.8)

  • -XX:MetaspaceSize(触发扩容/GC 的阈值,不是硬上限)
  • -XX:MaxMetaspaceSize(硬上限)

4. 运行时常量池:很多人混淆的点

你会听到"常量池在方法区里",但要注意几个历史变化:

  • 规范层面:运行时常量池属于方法区的一部分
  • HotSpot 实现上,JDK 版本间有多次迁移/调整

工程上你记住就行:

  • 类加载越多、常量越多,方法区/元空间压力越大

5. 实战:怎么判断是 Metaspace 泄露还是堆泄露

5.1 快速判断现象

  • 堆泄露:Java heap space,对象一直涨,Full GC 也回不去
  • 元空间泄露:Metaspace,类数量/类元数据一直涨

5.2 常用观测(低侵入)

  • 看类直方图:jcmd <pid> GC.class_histogram
  • 看类加载统计:jcmd <pid> VM.classloaders

你重点盯两件事:

  • 类数量是否持续增长
  • 是否存在"旧类加载器"长期存活(热部署/插件系统常见)

6. 一个常见根因:类加载器泄露

"类卸载"发生的前提通常是:

  • 对应的 ClassLoader 变成不可达

如果你有:

  • 静态集合缓存了某个类加载器加载的对象
  • 线程本地变量(ThreadLocal)引用了它
  • 第三方库注册监听器但不注销

就会导致:

  • 类加载器无法回收
  • 其加载的所有类元数据也无法卸载
  • 最终 Metaspace OOM

7. 总结

  • 方法区(元空间)放"类",堆放"对象"
  • JDK 1.7PermGen 改成 JDK 1.8Metaspace
  • Metaspace 也会 OOM,关键看类是否不断增加、类加载器是否泄露
  • 排障优先从 class_histogramclassloaders 入手
相关推荐
karry_k1 小时前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
karry_k1 小时前
PostgreSQL 在 MyBatis 中执行正常 SQL 失效:一次 DELETE USING 踩坑记录
java·后端
SamDeepThinking5 小时前
从源码到代码:MyBatis-Flex 与 MyBatis-Plus 的逐项对比
java·后端·程序员
她的男孩8 小时前
Spring Boot 接 Flowable 工作流:用 3 个注解搭一个请假审批流程
java·后端·架构
荣码10 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
plainGeekDev11 小时前
Gson → kotlinx.serialization
android·java·kotlin
小bo波20 小时前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
咖啡八杯21 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式