Skip to content

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 实现的方法)。
  • 与操作系统交互。
  • 特点
  • 与虚拟机栈类似,可固定或动态扩展。
  • 异常:StackOverflowErrorOutOfMemoryError
  • 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 后用元空间实现。
  • 交互:栈执行方法,堆存储对象,方法区提供元数据支持。