Java 虚拟机在执行 Java 程序时,会将所管理的内存划分为若干个不同的数据区域。其中,堆(Heap) 和 方法区(Method Area) 是所有线程共享的区域,属于 JVM 的共享内存区。
一、JVM 内存结构总览
| 区域名称 | 是否线程共享 | 说明 | 
|---|---|---|
| 程序计数器(Program Counter Register) | 否 | 当前线程执行字节码的行号指示器 | 
| Java 虚拟机栈(JVM Stack) | 否 | 每个线程私有,方法调用栈帧 | 
| 本地方法栈(Native Method Stack) | 否 | 用于调用 Native 方法 | 
| Java 堆(Heap) | 是 | 所有线程共享,用于存放对象实例 | 
| 方法区(Method Area) | 是 | 所有线程共享,存储类结构、静态变量、运行时常量池等 | 
| 运行时常量池(Runtime Constant Pool) | 是 | 方法区的一部分,存储编译时生成的常量和符号引用 | 
| 直接内存(Direct Memory) | 特殊 | 不在堆中,使用 JNI 分配的本地内存 | 
二、JVM 共享内存区域有哪些?
1. Java 堆(Heap)
- 
作用:Java 堆是 JVM 中最大的一块内存区域,用于存储对象实例和数组,是垃圾收集器管理的主要区域。 
- 
线程共享:是 
- 
创建时机:在 JVM 启动时创建。 
- 
内存划分: - 
新生代(Young Generation): - Eden 区:新对象优先在这里分配。
- Survivor 区(S0 和 S1):用作对象复制回收。
 
- 
老年代(Old Generation): - 存放生命周期较长的对象。
 
- 
(JDK 1.7 以前)永久代(PermGen) 
- 
(JDK 1.8 以后)元空间(Metaspace):使用本地内存替代永久代。 
 
- 
新生代对象分配与晋升过程
- 对象先分配到 Eden 区。
- 经一次 Minor GC 后存活的对象转移至 Survivor 区。
- 如果在 Survivor 区多次存活(年龄达阈值如 15),将晋升到老年代。
2. 方法区(Method Area)→ 元空间(Metaspace)
- 
作用:用于存储已被虚拟机加载的类信息、常量、静态变量、JIT 编译后的代码等。 
- 
线程共享: 是 
- 
JDK 变化: - JDK 1.7 之前方法区由永久代实现。
- JDK 1.8 开始使用 元空间(Metaspace) 替代,分配在本地内存中。
 
方法区存储内容包括:
- 类结构信息(字段、方法、访问修饰符等)
- 运行时常量池(Runtime Constant Pool)
- 静态变量
- 类初始化信息
- JIT 编译后的代码缓存等
3. 运行时常量池(Runtime Constant Pool)
- 
所属区域:方法区的一部分 
- 
线程共享: 是 
- 
内容: - 编译时生成的字面量(如字符串、整型常量等)
- 符号引用(类名、方法名等)
 
4. 字符串常量池(String Constant Pool)
- 
位置: - JDK 1.6 及以前:在永久代
- JDK 1.7 起:转移至堆中
 
- 
目的:避免重复创建相同字符串,提升效率与节省内存 
示例
            
            
              java
              
              
            
          
          String a = "ab";
String b = "ab";
System.out.println(a == b); // true,共享常量池引用字符串常量池为何从永久代迁移到堆?
- 永久代 GC 频率低,字符串长时间不回收。
- 堆空间更大、GC 更灵活,能提升字符串内存回收效率。
5. 直接内存(Direct Memory)
- 
作用 :通过 ByteBuffer.allocateDirect()分配的内存,常用于性能敏感的 I/O 操作。
- 
使用方式:通过 JNI 方式直接使用本地内存。 
- 
特点: - 不属于 JVM 规范定义的内存区域。
- 不是堆内存,也不在方法区。
- 容易出现 OOM 错误(如未手动释放)。
 
三、JVM 非共享内存区域(仅作对比)
| 区域 | 线程共享 | 用途 | 
|---|---|---|
| 程序计数器 | 否 | 记录当前线程执行的位置(字节码行号) | 
| 虚拟机栈 | 否 | 方法调用、局部变量、操作数栈等 | 
| 本地方法栈 | 否 | 执行 native 方法 | 
面试高频考点总结
| 问题 | 要点 | 
|---|---|
| JVM 哪些区域是线程共享的? | Java 堆、方法区(元空间)、运行时常量池、字符串常量池 | 
| 方法区和堆的区别? | 方法区存储类结构信息,堆存储对象实例;两者都线程共享 | 
| JDK 1.7/1.8 的内存结构变化? | 移除永久代,引入元空间;字符串常量池移动到堆 | 
| 什么是直接内存? | 不在堆、不在方法区,用 JNI 访问的本地内存 | 
| 方法区中存储的内容有哪些? | 类元数据、静态变量、常量池、JIT 编译代码等 |