一文彻底搞懂 Java 方法区Method Area与堆栈的关系及 JVM 内存结构总结之前的文章中我们详细讲解了Java 垃圾回收器GC以及堆内存结构。但 JVM 的内存结构并不仅仅只有堆和栈还有一个非常重要但经常被忽略的区域方法区Method Area在面试中这也是一个非常高频的问题例如JVM 内存结构有哪些什么是方法区方法区和堆有什么区别为什么会出现Metaspace OOMString 常量池在哪里这篇文章我们将从 JVM 内存模型出发深入理解方法区并与堆、栈建立完整关系。一、 JVM 内存结构在 JVM 中运行时内存结构通常可以简化为JVM Runtime Memory JVM │ ┌────────────────────────┐ │ Method Area │ │ (方法区 / 元空间) │ └────────────────────────┘ │ ┌────────────────────────┐ │ Heap │ │ (堆内存) │ └────────────────────────┘ │ ┌────────────────────────┐ │ Stack │ │ (虚拟机栈) │ └────────────────────────┘ │ Program Counter其中最核心的三个区域就是Heap Stack Method Area理解它们之间的关系是掌握 JVM 的关键。二、什么是方法区Method Area方法区是 JVM 规范中定义的一块内存区域用于存储类的元数据Class Metadata简单理解方法区 类的信息存储区方法区存储的内容方法区主要存储以下内容1 类信息例如类名 父类 接口 访问修饰符例如classUser{}JVM 会把User 类的信息存入方法区。2 运行时常量池每个类都有一个常量池例如字符串常量 final 常量 编译期常量例如Stringshello;字符串hello会进入常量池。3 静态变量例如classUser{staticintage18;}age存储在方法区。4 方法信息例如方法名 参数 字节码 返回值类型三、方法区与堆的关系很多人容易混淆这两个区域。我们先看一个简单例子UserusernewUser();JVM 内存分布如下方法区 │ │ User类信息 │ ▼ 堆 │ │ User对象 │ ▼ 栈 │ │ user引用变量关系总结内存区域存储内容方法区类信息堆对象实例栈方法调用和局部变量四、方法区与栈的关系方法调用过程publicvoidtest(){UserusernewUser();}执行流程1 方法调用test()方法入栈Stack ┌──────────────┐ │ test frame │ └──────────────┘2 访问类信息JVM 会去方法区查找 User 类信息。Method Area ┌──────────────┐ │ User.class │ └──────────────┘3 创建对象对象会分配到堆中。Heap ┌──────────────┐ │ User Object │ └──────────────┘4 栈中保存引用Stack ┌──────────────┐ │ user - heap │ └──────────────┘五、方法区的发展JDK版本变化方法区在不同 JDK 版本中实现不同。1 JDK7 及以前 —— 永久代PermGen在早期 JVM 中Method Area PermGen永久代特点固定大小容易 OOM常见错误java.lang.OutOfMemoryError: PermGen space2 JDK8 之后 —— 元空间MetaspaceJava 8 之后PermGen 被移除 Method Area → Metaspace特点项目PermGenMetaspace存储位置JVM内存本地内存是否固定是可扩展OOM概率高较低错误变为OutOfMemoryError: Metaspace六、字符串常量池在哪里这是一个经典面试题。不同版本不同JDK6字符串常量池 → 方法区JDK7字符串常量池 → 堆原因减少方法区压力提高 GC 效率七、方法区会被 GC 吗答案是会但回收条件非常苛刻GC 在方法区主要回收1 废弃常量例如String.intern()无引用的字符串可能被回收。2 无用类一个类满足以下条件才可能回收1 没有实例 2 没有类加载器引用 3 没有反射引用因此类卸载非常少见。八、方法区 OOM 常见原因常见异常OutOfMemoryError: Metaspace原因1 动态生成类过多例如CGLIB 动态代理2 类加载器泄漏例如Web应用热部署旧类加载器无法回收。3 大量反射生成类例如字节码增强九、方法区相关 JVM 参数限制元空间-XX:MaxMetaspaceSize256m设置初始大小-XX:MetaspaceSize128m查看 JVM 参数java -XX:PrintFlagsFinal十、JVM 内存结构总结到这里我们可以完整总结 JVM 的核心内存结构JVM Runtime Memory 线程私有 Program Counter Java Stack Native Stack 线程共享 Heap Method Area各区域作用区域作用程序计数器记录当前执行位置栈方法调用堆对象实例方法区类信息生命周期区域生命周期栈线程堆JVM方法区JVM十一、面试高频问题总结1 JVM 内存结构有哪些答程序计数器 虚拟机栈 本地方法栈 堆 方法区2 方法区存什么存储类信息 静态变量 运行时常量池 方法数据3 方法区和堆的区别方法区堆存类信息存对象元数据实例数据GC较少GC频繁4 为什么取消永久代原因1 内存固定 2 容易 OOM 3 不利于扩展5 什么是元空间JDK8 引入Metaspace使用本地内存存储类元数据。十二、最终总结JVM核心认知JVM 内存结构可以理解为三个核心部分类信息 → 方法区 对象实例 → 堆 方法调用 → 栈整体关系如下Method Area (类信息) │ Heap (对象实例) │ Stack (方法调用)理解这三者的关系就基本掌握了JVM 内存模型的核心设计思想。