【从零开始学习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)通常是因为创建了太多的大对象或者未能及时回收不再使用的对象

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


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

相关推荐
李日灐几秒前
<4>Linux 权限:从 Shell 核心原理 到 权限体系的底层逻辑 详解
linux·运维·服务器·开发语言·后端·面试·权限
寒秋花开曾相惜4 分钟前
(学习笔记)4.1 Y86-64指令集体系结构(4.1.6 一些Y86-64指令 )
linux·运维·服务器·开发语言·笔记·学习·安全
阿Y加油吧5 分钟前
算法二刷复盘:LeetCode 79 单词搜索 & 131 分割回文串(Java 回溯精讲)
java·算法·leetcode
-凌凌漆-9 分钟前
【QML】qml和C++中同时使用单例模式
java·c++·单例模式
那个失眠的夜10 分钟前
RESTful 语法规范 核心注解详解
java·spring·mvc·mybatis
我命由我1234513 分钟前
Android 开发问题:Unresolved reference: kapt
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
用户2986985301414 分钟前
Java 实现 ODT 转 PDF:一种简洁的技术实现方案
java·后端
城管不管15 分钟前
EasyExcel
java·开发语言·后端·easyexcel
鉴生Eric16 分钟前
拉孚BMA系统物联网架构:全面赋能传统楼宇BA系统的数字化转型
java·后端·struts
蓝桉~MLGT19 分钟前
Ai-Agent学习历程—— Harness和Memory介绍和应用 & vibe Coding工具选择
人工智能·学习