JVM内存结构
一、JVM 内存布局概述
- 重要性:内存是系统核心资源,JVM 内存布局确保高效运行。
- 划分:
- 线程私有:程序计数器、虚拟机栈、本地方法栈。
- 线程共享:堆、方法区(含元空间、代码缓存等堆外内存)。
- 生命周期:
- 随 JVM 启动创建,随退出销毁:堆、方法区。
- 随线程创建和销毁:程序计数器、虚拟机栈、本地方法栈。
二、程序计数器(PC 寄存器)
- 定义:线程私有的小块内存,记录当前线程执行的字节码指令地址。
- 作用:
- 存储下一条指令地址,供执行引擎读取。
- 支持 CPU 线程切换后继续执行。
- 特点:
- 内存占用极小,运行速度最快。
- 每个线程独立,不受干扰。
- 执行 Java 方法时记录指令地址,Native 方法时为
undefined
。 - 无
OutOfMemoryError
异常。 - 示例:反编译
.class
文件(javap -v
),查看 Code 区指令。
三、虚拟机栈
1. 概述
- 定义:线程私有,保存栈帧(Stack Frame),管理 Java 方法调用。
- 作用:存储局部变量、部分结果,参与方法调用和返回。
- 特点:
- 快速分配,仅次于程序计数器。
- 操作:入栈(方法执行)、出栈(方法结束)。
- 无垃圾回收。
- 异常:
StackOverflowError
:栈容量超限(-Xss
设置)。OutOfMemoryError
:无法扩展或创建新栈。
2. 栈帧
- 存储单位:栈帧,每个对应一个方法调用。
- 运行原理:
- 遵循“先进后出”。
- 当前栈帧(栈顶)有效,执行引擎操作针对它。
- 方法调用生成新栈帧,方法返回弹出栈帧。
- 结构:
- 局部变量表:
- 存储方法参数和局部变量(基本类型、引用类型)。
- 容量编译期确定,运行时不变。
- Slot:最小单位,32 位占 1 个,64 位(long、double)占 2 个。
this
占 Slot 0(实例方法),静态方法无this
。- Slot 可重用,节省空间。
- 与性能和 GC 根节点相关。
- 操作数栈:
- 后进先出,存储计算中间结果。
- 深度编译期确定(
max_stack
)。 - 通过入栈、出栈操作数据,无索引访问。
- 数据类型与指令严格匹配。
- 栈顶缓存:HotSpot 将栈顶元素缓存到 CPU 寄存器,提升效率。
- 动态链接:
- 指向运行时常量池的方法引用。
- 将符号引用转为直接引用。
- 绑定:
- 静态链接:编译期确定。
- 动态链接:运行期确定(晚期绑定)。
- 虚方法表:类加载时创建,优化动态分派。
- 方法返回地址:
- 存储调用者的 PC 值。
- 正常返回(
return
)或异常退出(异常表确定)。
- 附加信息:调试支持,依赖具体实现。
四、本地方法栈
- 定义:线程私有,管理 Native 方法调用。
- 作用:
- 调用非 Java 代码(如 C 实现的方法)。
- 与操作系统交互。
- 特点:
- 与虚拟机栈类似,可固定或动态扩展。
- 异常:
StackOverflowError
、OutOfMemoryError
。 - HotSpot 中与虚拟机栈合并。
- Native 方法:
- 接口:Java 调用外部代码。
- 原因:效率、与外部环境交互。
五、堆内存
1. 概述
- 定义:线程共享,存储对象实例,JVM 内存最大区域。
- 划分(优化 GC):
- 年轻代:Eden + Survivor (From/To),默认 8:1:1。
- 老年代:长期存活对象。
- 元空间(JDK 8 后):本地内存,非堆。
- 特点:
- 物理上可不连续,逻辑上连续。
- 可固定或扩展(
-Xmx
、-Xms
)。 - 溢出抛
OutOfMemoryError
。
2. 对象分配过程
- 流程:
- 新对象分配到 Eden。
- Eden 满时,Minor GC 回收无用对象,存活对象移至 Survivor。
- Survivor 间切换,年龄 +1。
- 达到年龄阈值(默认 15,
-XX:MaxTenuringThreshold
)或大对象(-XX:PretenureSizeThreshold
)进入老年代。 - 老年代满时,Major GC,若仍不足抛
OOM
。 - TLAB:
- Eden 内的线程私有缓冲区。
- 避免并发分配的线程安全问题。
- 默认占 Eden 1%(
-XX:TLABWasteTargetPercent
)。
3. GC 类型
- Minor GC:新生代回收。
- Major GC:老年代回收(常与 Full GC 混淆)。
- Full GC:整个堆和方法区回收。
- Mixed GC:新生代 + 部分老年代(G1 GC)。
4. 堆参数
- 设置:
-Xms
:初始堆大小(默认内存/64)。-Xmx
:最大堆大小(默认内存/4)。- 比例:
- 新生代:老年代 = 1:2(
-XX:NewRatio
)。 - Eden:From:To = 8:1:1(
-XX:SurvivorRatio
)。 - 自适应调整(
-XX:+UseAdaptiveSizePolicy
)。
5. 逃逸分析
- 定义:分析对象作用域,优化分配。
- 优化:
- 栈上分配:对象不逃逸,分配到栈上,随栈帧销毁。
- 同步省略:锁对象不逃逸,取消同步。
- 标量替换:对象分解为标量,分配到栈或寄存器。
- 参数:
-XX:+DoEscapeAnalysis
(JDK 6u23 后默认开启)。
六、方法区
1. 概述
- 定义:线程共享,存储类信息、常量、静态变量、JIT 代码。
- 特点:
- 逻辑上属堆,实际为非堆(Non-Heap)。
- 大小可固定或扩展,溢出抛
OutOfMemoryError
。 - 演进:
- JDK 6:永久代(堆内)。
- JDK 7:永久代,字符串常量池和静态变量移至堆。
- JDK 8:元空间(本地内存),常量池和静态变量在堆。
2. 内部结构
- 类型信息:类名、父类、修饰符、接口列表。
- 域信息:名称、类型、修饰符。
- 方法信息:名称、返回类型、参数、字节码、异常表。
- 运行时常量池:
- 存储编译期字面量和符号引用。
- 运行期动态添加(如
String.intern()
)。 - 溢出抛
OutOfMemoryError
。
3. 参数设置
- JDK 8:
-XX:MetaspaceSize
:初始大小(默认 20.75MB)。-XX:MaxMetaspaceSize
:最大值(默认无限制)。- 触高水位线触发 Full GC。
4. 垃圾回收
- 目标:
- 废弃常量:无引用即可回收。
- 无用类:实例全回收、类加载器回收、
Class
对象无引用。 - 控制:
-Xnoclassgc
禁用类回收。
七、总结
- 线程私有:
- 程序计数器:指令地址,无 OOM。
- 虚拟机栈/本地方法栈:栈帧管理方法调用,可能抛 SOE/OOM。
- 线程共享:
- 堆:对象存储,分代优化 GC。
- 方法区:类信息和常量,JDK 8 后用元空间实现。
- 交互:栈执行方法,堆存储对象,方法区提供元数据支持。