CrabNote螃蟹笔记

JVM 内存模型详解:从堆栈到元空间

深入理解 JVM 内存结构,包括堆、栈、方法区、程序计数器、本地方法栈,掌握 GC 工作原理。

JVM 内存模型详解 JVM(Java Virtual Machine)是 Java 程序运行的基石,理解它的内存模型是排查 OOM、调优 GC 的前提。 一、运行时数据区 JVM 在运行程序时会把内存划分为以下几个区域: 1. 程序计数器(PC Register) 一块较小的内存空间, 记录当前线程正在执行的字节码行号 。 线程私有 唯一不会发生 OOM 的区域 2. 虚拟机栈(VM Stack) 描述 Java 方法执行的内存模型。每个方法执行时都会创建一个 栈帧 : 栈帧包含: 局部变量表 :存放方法参数和方法内定义的变量 操作数栈 :存放计算过程中的中间结果 动态链接 :指向运行时常量池的方法引用 方法出口 :方法返回的地址 3. 本地方法栈 为 Native 方法服务的栈区,与虚拟机栈作用类似。 4. 堆(Heap) 所有线程共享 ,存放对象实例,是 GC 的主战场。 现代 JVM 将堆划分为: 新生代 :Eden + Survivor 0 + Survivor 1(8:1:1) 老年代 :长期存活的对象 5. 方法区(元空间) 存储 类信息、常量、静态变量、JIT 编译后的代码 。 JDK 8 之后,方法区的实现从永久代(PermGen)改为 元空间(Metaspace) ,使用本地内存,避免了永久代 OOM 的问题。 二、对象创建过程 当执行 时,JVM 内部发生的事情: 1. 类加载检查 :检查 User 类是否已加载、解析、初始化 2. 分配内存 :在堆中划出固定大小的内存 指针碰撞(内存规整时) 空闲列表(内存碎片时) 3. 内存空间初始化 :清零操作 4. 对象头设置 :设置哈希码、GC 分代年龄、锁状态 5. 执行 方法 :调用构造函数 三、GC 工作原理 判断对象是否存活 引用计数法 (已淘汰):循环引用问题 可达性分析 (主流):从 GC Roots 出发是否能到达 GC Roots 包括: 虚拟机栈中的局部变量 方法区的类静态属性 方法区的常量 本地方法栈的 JNI 引用 垃圾收集算法 | 算法 | 描述 | 适用场景 | | | | | | 标记 清除 | 标记存活,清除其他 | 老年代 | | 标记 整理 | 清除后整理碎片 | 老年代 | | 复制算法 | 两块区域来回复制 | 新生代 | | 分代收集 | 综合以上算法 | 现代 JVM | 四、实战:OOM 排查 报错 : 排查思路 : 1. 添加 JVM 参数: 2. 使用 MAT / VisualVM 分析堆转储文件 3. 定位大对象,优化代码逻辑 五、小结 | 区域 | 私有/共享 | 存储内容 | 异常 | | | | | | | 程序计数器 | 私有 | 字节码行号 | 不会 OOM | | 虚拟机栈 | 私有 | 栈帧 | StackOverflowError / OOM | | 本地方法栈 | 私有 | Native 方法 | OOM | | 堆 | 共享 | 对象实例 | OOM | | 方法区 | 共享 | 类信息、常量 | OOM | 小宇宙提示:掌握 JVM 内存模型,是写出高性能、高可用 Java 应用的前提。