【从零开始学习JVM】内存模型+堆栈的区别


🌈 个人主页: Hygge_Code
🔥 热门专栏:从0开始学习Java | Linux学习 | 计算机网络
💫 个人格言: "既然选择了远方,便不顾风雨兼程"

文章目录

内存模型

模型构成总览 🥝

根据 JDK 8 规范,JVM运行时内存共分为 虚拟机栈、堆、元空间、程序计数器、本地方法栈 五个部分。还有一部分内存叫 直接内存 ,属于操作系统的本地内存,也是可以直接操作的。

由上面这张图片,我们可知JVM的内存模型主要分为以下几个部分:

程序计数器

  • 核心定位线程私有、生命周期与线程绑定的最小内存区域,是JVM中唯一不会抛出OutOfMemoryError的区域
  • 核心作用:记录当前线程正在执行的字节码指令地址(行号)。当线程切换、CPU时间片轮转时,程序计数器会保存线程的执行位置,让线程恢复时能从断点继续执行
  • 补充特性
    • 执行Java方法时,计数器记录的是正在执行的虚拟机字节码指令地址
    • 执行Native(本地)方法时,计数器值为undefined(无定义)🐦‍🔥
    • 线程私有,因此多线程环境下不会出现线程安全问题

  • 核心定位线程私有、生命周期与线程完全一致的内存区域,也叫Java栈
  • 核心作用:存储Java方法执行的栈帧(Stack Frame),每个Java方法从调用到执行完成,对应一个栈帧在虚拟机栈中 ( 入栈→出栈 ) 的过程
  • 栈帧核心结构
    1. 局部变量表:存储方法的入参、局部变量、基本数据类型和对象引用(reference)
    2. 操作数栈:字节码执行的临时数据区,用于运算、方法调用传参
    3. 动态链接 :指向运行时常量池的方法引用,支持方法的动态绑定
    4. 方法返回地址:方法执行结束后,恢复调用者的执行位置
  • 异常场景
    • StackOverflowError:栈深度超过虚拟机限制(如无限递归)
    • OutOfMemoryError:虚拟机栈无法动态扩展(如创建大量线程耗尽内存)

本地方法栈(Native Method Stack)

  • 核心定位线程私有,作用与虚拟机栈几乎完全一致,HotSpot虚拟机中直接将其与虚拟机栈合并实现
  • 核心作用:为JVM调用的Native(本地)方法(如C/C++实现的底层方法)提供内存支持,存储Native方法执行的栈帧
  • 补充说明
    • 服务于非Java语言编写的方法,是JVM与操作系统底层交互的桥梁
    • 异常类型与虚拟机栈完全一致,同样会抛出StackOverflowErrorOutOfMemoryError

堆(Heap)

  • 核心定位:****线程共享的最大内存区域,是垃圾回收(GC)的主战场,也是OOM最常发生的区域
  • 核心作用:存储所有Java对象实例、数组,是Java程序内存分配的核心区域
  • 补充特性
    • 图中堆区域包含 运行时常量池(JDK8后,运行时常量池从方法区迁移至堆中)
    • 堆被垃圾回收器划分为年轻代(Eden + S0/S1 Survivor)、老年代,用于分代回收
    • 可通过-Xms/-Xmx参数设置堆的初始/最大大小
    • 典型OOM:java.lang.OutOfMemoryError: Java heap space(内存泄漏、对象过多)

方法区(元空间/MetaSpace)

  • 核心定位:****线程共享的内存区域,JDK8后用 [ 元空间 ] 替代了永久代(PermGen),直接使用本地内存,不再受JVM堆大小限制
  • 核心作用:存储类的元数据信息,包括:类结构、字段、方法、构造器、即时编译(JIT)后的机器码等
  • 补充特性
    • 元空间默认无大小上限,可通过-XX:MaxMetaspaceSize限制最大内存
    • 支持类卸载:当类加载器不再被引用时,对应的类元数据会被GC回收
    • 典型OOM:java.lang.OutOfMemoryError: Metaspace(动态生成大量类、CGLib代理滥用)

运行时常量池

  • 核心定位堆内存的子区域,用于存放编译期生成的各种字面量和符号引用
  • 核心作用:存储编译期生成的字面量(如字符串、数字)、符号引用量(类/方法/字段的引用) ,并在运行期动态生成新的常量(如String.intern()
  • 补充说明
    • JDK7及之前,运行时常量池属于方法区(永久代);JDK8后,随永久代移除,被迁移至Java堆中
    • 是类加载过程中解析阶段的核心依赖,用于将符号引用转换为直接引用

直接内存(Direct Memory)

  • 核心定位:****不属于JVM运行时数据区的本地内存,由操作系统直接管理
  • 核心作用:通过Java NIO的ByteBuffer.allocateDirect()分配,用于堆外内存操作,避免Java堆与Native堆之间的数据拷贝,大幅提升I/O性能(如大文件读写、网络通信)
  • 补充特性
    • 不受JVM堆大小限制,但受操作系统总内存限制,可通过-XX:MaxDirectMemorySize设置上限
    • 内存回收依赖Cleaner机制(ByteBuffer对象被GC时自动回收),或手动调用clean()方法
    • 典型OOM:java.lang.OutOfMemoryError: Direct buffer memory(堆外内存分配超限)

JVM内存模型里的堆和栈有什么区别?🤔

  • 用途 主要用于存储局部变量、方法调用的参数、方法的返回地址以及一些临时数据。每当一个方法被调用,一个栈帧就会在栈中创建,用于存储该方法的信息,当方法执行完毕,栈帧也会被移除。用于存储对象的实例(包括类的实例和数组),当你使用 new关键字创建一个对象时,对象的实例就会在堆上分配空间。

  • 声明周期 上的数据具有确定的生命周期,当一个方法的调用结束时,其对应的栈帧就会被销毁,栈中存储的局部变量也会随之消失。中的对象生命周期不确定,对象会在垃圾回收机制(GC)检测到对象不再被引用时才会被回收。

  • 存取速度 的存取速度通常比堆快,因为栈的操作简单快速。而的存取速度比较慢,因为对象在堆上的分配和回收需要更多的时间,而且垃圾回收机制的运行也会影响性能。

  • 存储空间 的空间相对固定且较小。当栈溢出 时(StackOverflowError),通常是因为递归过深或局部变量过大。 的空间较大,动态扩展。堆溢出OutOfMemoryError)通常是因为创建了太多的大对象或者未能及时回收不再使用的对象

  • 可见性中的数据对线程是私有的,每个线程都有自己的栈空间。中的数据对线程是共享的,所有线程都可以访问堆上的对象


如果我的内容对你有帮助,请 点赞 , 评论 , 收藏 。创作不易,大家的支持就是我坚持下去的动力!

相关推荐
迈巴赫车主2 小时前
蓝桥杯 19717 挖矿java
java·开发语言·数据结构·算法·职场和发展·蓝桥杯
yaaakaaang2 小时前
四、建造者模式
java·建造者模式
Sag_ever2 小时前
Java String 类详解:字符串常用方法 + 不可变性 一网打尽
java·开发语言
2501_921649492 小时前
从WebSocket到SQL查询:金融数据落库存储及查询接口全流程开发
java·sql·websocket·程序人生·spring cloud·金融·系统架构
jinanwuhuaguo2 小时前
OpenClaw v2026.4.1 深度剖析报告:任务系统、协作生态与安全范式的全面跃迁
java·大数据·开发语言·人工智能·深度学习
努力不熬夜 ^_^2 小时前
我用 GLM-5.1 重构了我的 AI 项目
java·重构·react·glm·vibe coding·coding plan
小雷君2 小时前
SpringBoot + SpringSecurity + JWT 完整整合实战(生产级无状态认证)
java·spring boot·spring
澄风2 小时前
IDEA 代码模板配置教程(prs快捷生成private String)
java·ide·intellij-idea
弹简特2 小时前
【JavaEE25-后端部分】从“统一回执单”到“统一投诉处理”:Spring Boot 轻松搞定统一返回格式和统一异常处理
java·spring boot·后端·统一返回格式·统一异常