02-JVM运行时内存划分
2026/9/27大约 4 分钟
一,JVM运行时内存划分

线程共享区:
- 方法堆
- java堆
线程独占区:
- 虚拟机栈
- 本地方法栈
- 程序计数区
1.1 虚拟机栈
- 虚拟机栈描述的是Java方法执行的动态内存模型;
- 栈帧:每一个方法的执行都会创建一个栈帧,伴随着方法从创建(压入虚拟机栈)到执行完成(弹出虚拟机栈)。用于存储局部变量表,操作数栈,动态链接,方法出口等。栈帧包含一个方法的主要信息。
- 局部变量表:存放编译期可知的各种基本数据类型,引用类型,方法的出口地址;局部变量表的内存空间,在编译期完成分配,当进入一个方法时,这个方法,需要在帧中分配的空间时固定的,在方法运行期间不会改变局部变量表的大小。
- StackOverFlowError:通常是递归调用方法不合理,导致虚拟机栈空间不够了
1.2 本地方法栈
在hotspot中,虚拟机栈和本地方法栈是同一块区域;

虚拟机栈和本地方法栈区别:
- 虚拟机栈是为虚拟机执行java方法服务的;
- 本地方法栈是为虚拟机执行native方法服务的;
1.3 程序计数区
- 程序计数器是一个比较小的内存空间,可以将其看作是当前线程锁执行的字节码的行号指示器。
- 程序计数器位于线程独占区
- 此区域是唯一没有OutOfMemroyError(内存溢出错误)错误的区域
1.4 堆
Java中堆内存划分:

在整个JVM的堆内存中实际上将内存分为了三部分:
新生代
新生代几乎是所有Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕死的性质,新生代是 GC 收集垃圾的频繁区域。新生代分为三个区:Eden 和 两个存活区(From 和 To),默认分别占内存的80%、10%、10% ,可以通过参数
–XX:SurvivorRatio来设定 。注: JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块Survivor区域是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
老年代
存储被长时间使用的对象, 默认新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,该值可以通过参数 –XX:NewRatio 来指定.
元空间(JDK1.8之前叫永久代)
存储一些方法中操作的临时对象等,JKD1.8之前是占用JVM内存,JDK1.8之后直接占用物理内存。取消永久代的目的是将 HotSpot和JRockit 的两个虚拟机标注联合为一个。
1.5 方法区
方法区主要存储已经被虚拟机加载的类信息、常量、静态变量、以及编译器编译后的代码等数据。
| 内容 | JDK 6及以前 | JDK 7 | JDK 8+ |
|---|---|---|---|
| 方法区概念 | ✅ 有 | ✅ 有 | ✅ 有 |
| 方法区实现 | 永久代 | 永久代(逐渐移除) | 元空间 |
| 类元数据存储 | 永久代 | 永久代 | 元空间 |
| 字符串常量池 | 永久代 | 堆 | 堆 |
| 静态变量 | 永久代 | 永久代 | 堆 |
| Class对象存储 | 堆 | 堆 | 堆 |
| 实例对象存储 | 堆 | 堆 | 堆 |
| 误区 | 真相 | |
|---|---|---|
| "方法区就是永久代" | ❌ 方法区是规范,永久代是JDK 7及以前的实现 | |
| "元空间就是方法区" | ✅ 元空间是JDK 8对方法区的实现 | |
| "字符串常量池在元空间" | ❌ JDK 7开始就在堆中,不再在方法区/永久代 | |
| "静态变量在元空间" | ❌ JDK 8开始静态变量在堆中(跟随Class对象) | |
| "方法区不回收" | ❌ 方法区(元空间)可以回收类元数据(类卸载) | |