方法区 / 元空间: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 入手
相关推荐
6+h1 小时前
【java IO】缓冲流详解
java·开发语言
JNU freshman2 小时前
Ceph 中与“重构/恢复/回填/重平衡”相关的参数表
java·ceph·重构
寻见9032 小时前
Java为什么能“一次编写,到处运行”?JVM到底解决了什么核心痛点?
java·jvm·java ee
xjdkxnhcoskxbco2 小时前
Java 多线程“八锁”问题深度解析
java·开发语言·多线程
AI克斯2 小时前
【通俗易懂】注解(@)的理解
java
人还是要有梦想的2 小时前
QT的起源
开发语言·qt
spencer_tseng2 小时前
‘<>‘ operator is not allowed for source level below 1.7
java
柏箱2 小时前
文件上传漏洞入门:(upload-labs Pass-1 & Pass-2)
开发语言·前端·javascript
人道领域2 小时前
Day | 07 【苍穹外卖:菜品套餐的缓存】
java·开发语言·redis·缓存击穿·springcache