方法区是JVM规范定义的逻辑区域,用于存储类元信息、常量池、静态变量、JIT编译代码等;PermGen(JDK 1.7及之前)和Metaspace(JDK 1.8+)是HotSpot对其的两种实现,前者位于JVM堆内、大小固定易OOM,后者使用本地内存、可动态扩展但需防系统内存耗尽。

方法区是JVM规范中定义的逻辑区域,用于存储类元信息、常量池、静态变量、JIT编译代码等。它本身不规定具体实现方式,而PermGen和Metaspace是HotSpot虚拟机在不同JDK版本中对方法区的两种具体实现。
存储位置与内存归属不同
PermGen位于JVM堆内存内部,与新生代、老年代连续分配,属于Java进程的堆空间;Metaspace则使用本地内存(Native Memory),即操作系统直接管理的物理内存,完全脱离JVM堆。
- PermGen受-Xmx限制,其大小由-XX:PermSize和-XX:MaxPermSize硬性设定,无法动态增长
- Metaspace默认无上限,只受限于系统可用内存;可通过-XX:MetaspaceSize(触发GC的初始阈值)和-XX:MaxMetaspaceSize(强制上限)控制
类元数据与常量池的存放范围变化
JDK 1.7起已开始拆分永久代职责,到JDK 1.8彻底由Metaspace接管部分功能,但并非“全盘接收”:
- 类的元数据(类名、字段、方法签名、注解、符号引用表等)始终由PermGen或Metaspace承载
- 运行时常量池(Runtime Constant Pool)仍属方法区一部分,在PermGen中完整存在;Metaspace中仅保留与类结构强相关的常量(如符号引用),而字面量(如字符串字面量)自JDK 1.7起就已移入Java堆
- 静态变量(static fields)在JDK 1.7之前存于PermGen,之后统一归入Java堆,与Metaspace无关
垃圾回收机制与稳定性差异
PermGen的GC与老年代绑定,一旦触发就是Full GC,开销大且不可控;Metaspace支持独立回收,且仅在类加载器被卸载、对应类元数据真正无用时才释放空间。
- PermGen溢出报错为java.lang.OutOfMemoryError: PermGen space,常见于频繁热部署、大量动态代理(CGLIB)、JSP编译等场景
- Metaspace虽极少OOM,但若未设-XX:MaxMetaspaceSize且持续加载新类(如微服务网关反复加载SPI实现),仍可能耗尽本地内存,报java.lang.OutOfMemoryError: Metaspace
- Metaspace的GC不导致Stop-The-World暂停,对应用吞吐影响更小
参数配置与运维关注点迁移
从PermGen到Metaspace,调优重点从“防溢出”转向“控增长”:
- 旧参数-XX:PermSize/-XX:MaxPermSize失效,需替换为-XX:MetaspaceSize/-XX:MaxMetaspaceSize
- 监控手段变化:jstat -gc中查看MC(Metaspace Capacity)、MU(Metaspace Used);jmap -histo:live不再显示PermGen类统计,改用jcmd
VM.native_memory summary查看本地内存分布 - 典型误配:仅设MetaspaceSize却不设MaxMetaspaceSize,可能导致容器环境因本地内存超限被OOM Killer强制终止










