枪战英雄
99.99M · 2026-03-28
JVM 在执行 Java 程序时,会将内存划分为若干个不同的运行时数据区。这些区域各有用途,有的随线程创建和销毁(线程私有),有的随 JVM 启动而存在(线程共享)。理解这些区域是进行 JVM 调优、排查内存问题的基石。
| 区域名称 | 线程共享 | 存储内容 | 主要异常 |
|---|---|---|---|
| 程序计数器 | 私有 | 当前线程执行的字节码行号(或 Native 方法状态) | 无 |
| Java 虚拟机栈 | 私有 | 方法调用的栈帧(局部变量表、操作数栈、动态链接、方法出口) | StackOverflowError OutOfMemoryError |
| 本地方法栈 | 私有 | 为 Native 方法服务,类似虚拟机栈 | 同上 |
| Java 堆 | 共享 | 对象实例、数组(GC 主要管理区域) | OutOfMemoryError: Java heap space |
| 方法区 | 共享 | 类元数据、运行时常量池、静态变量(JDK 7+ 移至堆)、即时编译后的代码 | OutOfMemoryError: Metaspace(JDK8+) |
此外,还有直接内存(Direct Memory),不属于 JVM 运行时数据区,但常与 NIO 一起使用,也可能导致内存溢出。
undefined。OutOfMemoryError 情况的区域。生命周期与线程相同。线程私有,生命周期与线程相同。
结构:每个方法执行时,JVM 会同步创建一个栈帧(Stack Frame),用于存储:
异常:
StackOverflowError(常见于递归过深或死循环)。OutOfMemoryError(某些实现支持动态扩展)。native 方法服务。StackOverflowError / OutOfMemoryError)。线程共享,是 JVM 管理内存中最大的一块。
作用:存放所有对象实例和数组(几乎所有对象都在这里分配,但存在栈上分配、标量替换等优化技术,可使部分对象不进入堆)。
GC 管理:堆是垃圾回收的重点区域,常被细分为:
异常:如果堆无法继续扩展(-Xmx 限制)且无法分配新对象 → OutOfMemoryError: Java heap space。
线程共享,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
版本演变:
-XX:PermSize 和 -XX:MaxPermSize 限制。-XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 控制。运行时常量池(Runtime Constant Pool) :方法区的一部分,存放 Class 文件中的常量池表(字面量、符号引用),以及运行时生成的新常量(如 String.intern() 的结果)。
异常:方法区内存不足 → OutOfMemoryError: Metaspace(JDK8+)或 PermGen space(JDK7-)。
ByteBuffer.allocateDirect() 分配,使用 Native 堆内存。-Xmx 大小相近。OutOfMemoryError: Direct buffer memory。int、float、字符串引用)和符号引用。intern() 方法的字符串引用。在 JDK 7 中从永久代移至堆,JDK 8+ 仍在堆中。两者关系:字符串字面量在类加载时,会从运行时常量池中取出符号,去字符串常量池中查找或创建实际的 String 对象,然后将对象的引用回填到运行时常量池。
| 项目 | JDK 6 及以前 | JDK 7 | JDK 8+ |
|---|---|---|---|
| 方法区实现 | 永久代(PermGen) | 永久代,但逐步移除 | 元空间(Metaspace) |
| 方法区位置 | JVM 堆内 | JVM 堆内 | 本地内存 |
| 字符串常量池位置 | 永久代 | 堆 | 堆 |
| 静态变量位置 | 永久代 | 堆 | 堆 |
-Xss 或优化递归。-Xmx,分析 heap dump。MaxMetaspaceSize,排查类加载器泄漏。-XX:MaxDirectMemorySize。text
┌─────────────────────────────────────────────────────┐
│ JVM 运行时数据区 │
├─────────────────────────────────────────────────────┤
│ 线程私有 线程共享 │
├─────────────────┬───────────────────────────────────┤
│ 程序计数器 │ 堆 │
│ Java虚拟机栈 │ (对象实例、数组) │
│ 本地方法栈 ├───────────────────────────────────┤
│ │ 方法区(元空间) │
│ │ (类元数据、常量池、即时编译代码) │
└─────────────────┴───────────────────────────────────┘
掌握 JVM 运行时数据区的划分和特性,是进行内存调优、定位内存泄漏、选择垃圾回收器的基础。实际应用中,应结合 GC 日志和堆转储文件,精准分析问题所在。