方法区存放类的元数据,包括类结构、常量、静态变量、JIT编译后的代码;HotSpot 1.8+用Metaspace替代永久代,使用本地内存并缓解GC压力。

方法区存放类的元数据,不是代码本身,也不是运行时创建的对象。
方法区存什么:类结构、常量、静态变量、JIT编译后的代码
它保存的是 Class 对象加载后生成的结构信息,比如字段名、方法签名、访问标志、注解、运行时常量池(注意:不是字符串常量池,后者在堆中)、static final 基本类型字面量。JVM 规范不强制要求实现方式,HotSpot 1.8+ 用 Metaspace 替代永久代,直接使用本地内存,避免 java.lang.OutOfMemoryError: PermGen space。
-
static变量值存在方法区,但引用的对象实例仍在堆中 -
String.intern()在 JDK 7+ 后,字符串常量池移到堆里,和方法区无关了 - JIT 编译后的本地代码(如 C1/C2 编译器产出)也缓存在方法区
为什么改用 Metaspace:解决永久代的硬限制和 GC 压力
永久代(PermGen)大小固定(如 -XX:MaxPermSize=256m),容易因动态类加载(如 OSGi、热部署、大量反射)撑爆。Metaspace 默认只受物理内存限制,且 GC 不扫描其内部结构——类卸载只发生在 Full GC 且满足条件时,由 ClassLoader 是否还存活决定。
- 可显式控制上限:
-XX:MaxMetaspaceSize=512m,不设则无上限 -
-XX:MetaspaceSize是触发首次 Metaspace GC 的阈值(类似初始堆大小) - 类卸载的前提是:该类所有实例已被回收 +
ClassLoader实例不可达 + 该类未被其他类引用
常见误判:String、static、Lambda 都不“全在”方法区
容易把“静态”“常量”“字节码”混为一谈。例如:
立即学习“Java免费学习笔记(深入)”;
-
String s = "hello";→ 字符串对象在堆,字面量"hello"在字符串常量池(JDK 7+ 在堆) -
public static final int VAL = 1;→ 编译期常量,直接内联到调用处,不占方法区空间 -
public static final List→LIST = Arrays.asList("a"); LIST引用存在方法区,但Arrays.asList()返回的实例在堆 - Lambda 表达式会生成私有静态方法(在方法区),但每次执行仍可能创建新函数对象(在堆)
真正难排查的是类加载器泄漏导致 Metaspace 持续增长,而 java.lang.OutOfMemoryError: Metaspace 出现时,往往说明已有大量类没被卸载——这时候看 ClassLoader 的引用链比看方法区大小更有价值。









