JVM 内存区域------一计两栈一堆一区
首先来认识 JVM 的"五大金刚"(一计两栈一堆一区):
- 🧠 程序计数器
- 📚 虚拟机栈
- 🌍 本地方法栈
- 🗑️ 堆
- 📦 方法区(包含运行时常量池)
- JDK 7及以前:永久代(PermGen)
- JDK 8及以后:整片方法区(含运行时常量池)从永久代搬家到元空间,住进了本地内存(告别了 PermGen 的"空间焦虑")
🧠 程序计数器
它是 JVM 里的"小秘书",专门负责记录当前线程执行到哪一行代码。
唯一一个不会抛出 OOM 的区域,确保线程切换后能准确回到"案发现场"。
所以它是线程私有的,谁也不抢谁的进度条。
📚 虚拟机栈
虚拟机栈由一个个栈帧 组成,每个方法从"开演"到"谢幕",就对应一个栈帧的入栈 和出栈 。
每个栈帧的结构就像一个小型工作台:
| 栈帧结构 |
|---|
| 局部变量表 |
| 操作数栈 |
| 动态链接 |
| 返回地址 |
局部变量表
存放局部变量的"临时储物柜"。
操作数栈
真正做计算的地方,可以理解为 JVM 的"草稿纸"。
动态链接
它是符号引用 变身直接引用的桥梁,指向执行该方法所需的信息(住在运行时常量池里)。
返回地址
方法演完该回哪儿,它就指哪儿。
🌍 本地方法栈
专门为 Native 方法服务的区域,存储要调用的 C/C++ 方法(JVM 的"外交部门")。
🗑️ 堆
对象的"家园",所有对象实例和数组都住在这里。
不同 GC 收集器会把它布置成不同的"户型结构"。
📦 方法区
存放"静态资源"和"元信息"的高级仓库,内容包括:
- 类信息(版本、字段、方法、接口等)
- 常量、静态变量
- 即时编译器编译后的代码缓存
- 运行时常量池
运行时常量池
这里是编译期生成的各种"字面量"和"符号引用"的集合地,运行时解析后还会加入直接引用。
java
plain
运行时常量池内容一览:
┌─────────────────────────────────┐
│ 1. 字面量 │
│ - 字符串:"Hello" │
│ - 数字:100, 3.14 │
│ - 布尔:true/false │
├─────────────────────────────────┤
│ 2. 符号引用 │
│ ├─ 类符号引用:Class_info │
│ ├─ 字段符号引用:Fieldref │
│ ├─ 方法符号引用:Methodref │
│ └─ 接口方法引用:InterfaceMethodref│
├─────────────────────────────────┤
│ 3. 直接引用(运行时解析后加入) │
│ - 方法指针地址 │
│ - 字段偏移量 │
└─────────────────────────────────┘
返回地址就是方法执行完毕后该回到的"起点"。
🤔 延伸思考
为什么动态链接要用"符号引用"而不是"直接引用"?
永久代为何演变成元空间?
记住这个"一计两栈一堆一区",你就握住了 JVM 内存世界的钥匙 🗝️。下次面试时,可以轻松说出:"它们各司其职,有条不紊,就像一支高效运转的小分队!"