当前位置: 首页 > news >正文

JVM 运行时数据区 —— 5 大块内存

一句话

JVM 的内存按用途分为 5 块区域:程序计数器、Java 虚拟机栈、本地方法栈(线程私有)、堆、元空间(线程共享)。每一块都有各自的用途和异常。


5 大区域总览

JVM 运行时数据区 │ ├── 线程私有(每个线程独立一份) │ ├── ① 程序计数器 ← 很小,记录当前线程执行到哪一行 │ ├── ② Java 虚拟机栈 ← 方法调用、局部变量、对象引用 │ └── ③ 本地方法栈 ← native 方法用的栈 │ └── 线程共享(所有线程共用) ├── ④ 堆(Heap) ← 所有对象实例(new 出来的都在这里) └── ⑤ 元空间(Metaspace)← 类结构信息、方法字节码、常量池

线程私有区域

① 程序计数器

作用​:记录当前线程执行到哪一行字节码指令。

publicintadd(inta,intb){intc=a+b;// ← 当前执行到这里,程序计数器就记录这一行returnc;}

为什么需要​?多线程切换时,切回来要知道从哪继续执行。

特点​:JVM 规范里唯一不会 OOM 的区域。很小。

② Java 虚拟机栈

作用​:每个方法调用时分配一个​栈帧​,方法执行完栈帧出栈。

methodA 进入: 栈:[methodA 栈帧] methodA 调用 methodB: 栈:[methodA 栈帧] [methodB 栈帧] ← methodB 在栈顶 methodB 结束: 栈:[methodA 栈帧] ← methodB 出栈 methodA 结束: 栈:[] ← methodA 出栈

栈帧里有什么?

一个栈帧: ├── 局部变量表 ← 方法里的局部变量和对象引用 ├── 操作数栈 ← 计算时的临时"草稿纸" ├── 动态链接 ← 指向运行时常量池的引用 └── 返回地址 ← 方法结束后回到哪里

关键理解​:对象在堆里,栈里只存对象的引用地址(“遥控器”)。

Useruser=newUser();// 栈里: [user — 地址 0x1234] ← 遥控器// 堆里: [User 对象] ← 房子

异常​:递归太深 →StackOverflowError

③ 本地方法栈

和虚拟机栈功能一样,但给native 方法使用的。

// native 方法 —— 用 C/C++ 实现的 Java 方法publicnativeinthashCode();// Object 类,底层 C++ 实现publicstaticnativevoidsleep(longmillis);// Thread 类

Java 方法调用用虚拟机栈,native 方法调用用本地方法栈——两者分开管理,互不干扰。


线程共享区域

④ 堆(Heap)

所有对象实例都放在堆里。

newUser();// → 堆newArrayList<>();// → 堆newint[10];// → 堆

堆内部按"对象的存活时间"分为两代:

堆 ├── 新生代(朝生夕死的对象) │ ├── Eden(80%) ← 所有新对象先放这里 │ └── Survivor │ ├── S0(10%) ← From │ └── S1(10%) ← To │ └── 老年代(熬过多次 GC 的对象)

堆是最容易出问题的区域​——OOM 绝大多数就是堆满了。具体分代和对象流转见下一篇。

⑤ 元空间(Metaspace)

作用​:存放类的结构信息——可以理解为"对象的图纸"。

堆里: [User 对象1] [User 对象2] ... [User 对象10000] ← 10000 份实例 元空间:[User 类的结构信息] ← 只有 1 份图纸

元空间里存的是:

  • 类的全限定名(com.example.User
  • 类的字段描述(id: Long,name: String
  • 类的方法字节码
  • 常量池

JDK7 vs JDK8 的变化​:

JDK7(方法区/永久代)JDK8(元空间)
位置在堆里不在堆里,用本地内存
大小限制有上限,容易永久代 OOM默认不限制(除非设置 MaxMetaspaceSize)
字符串常量池在方法区移到了堆里(避免 OOM)
静态变量在方法区移到了堆里

面试问答

一个对象在哪个区域?

代码JDK8 存在哪
new User()(对象实例)
User.class(Class 对象)
类的字段/方法/字节码(“图纸”)元空间
"hello"(字符串常量)​(JDK8 字符串常量池在堆里)
static int count(静态变量)​(JDK8 移到了堆里)

栈和堆的区别?

栈(虚拟机栈):存方法调用和局部变量,"控制" 堆:存对象实例,"数据" 栈里的引用指向堆里的对象

参考来源

  • 《深入理解 Java 虚拟机》第 2 章
  • JDK8 JVM 规范
http://www.jsqmd.com/news/1098859/

相关文章:

  • C++ Primer Plus 重读精讲 _ 指针进阶全集:三类const指针辨析、指针数组数组指针硬核区分、指针地址传参、工控函数双向改参实战
  • VMware虚拟机组网通信全链路解析(ESXi 7.0+vSphere 8.0实测验证)
  • 面向对象——多态
  • Focus架构:视觉语言模型的高效加速方案
  • 群辉Nas部署GitServer随笔
  • 别再被Python列表拷贝坑了!浅拷贝深拷贝,一个不注意就让你代码崩盘
  • 如何快速查找 *Bash* 命令的*类型*?
  • File和IO
  • 与你的 Elasticsearch 数据对话:使用 Google ADK 和 MCP 构建一个实时语音 agent ,分为 3 个组件
  • 5分钟快速上手:RedisDesktopManager-Windows终极可视化数据库管理工具完整指南
  • 告别串口乱码!STM32F401RCT6用Arduino框架点灯+串口打印保姆级教程
  • C#工业视觉实战:集成工业相机与YOLOv8实现缺陷检测系统
  • 探索兴趣爱好的内涵
  • 廖雪峰Python2教程PDF!20行代码秒杀C语言1000行,速度慢?谁在乎
  • 别再让激光器‘发烧’了!手把手教你用运放搭建高精度恒流源(附LTspice仿真文件)
  • 如何生成字母或数字的*序列*?
  • Dify平台大模型接入实战:从云端API到本地部署全流程指南
  • Postman便携版终极指南:Windows用户的免安装API开发解决方案
  • 别再只会用三极管了!用JFET搭个恒流源给LED调光,实测效果稳如老狗
  • 电脑弹窗拦截工具绿色免费超好用
  • 48.可直接落地!IEC61131-3 ST 完整源码|PLC 物料分拣 + PID 调速 + Modbus 通信
  • 零基础入门MySQL数据分析:从SQL语法到电商实战项目
  • SH9递归对抗驱动的活系统:九层架构理论体系深度研究报告(世毫九实验室原创研究)
  • linux中TCP通信
  • Python之rickshaw包语法、参数和实际应用案例
  • 基于PANDAS的QAbstractTableModel实现高级TableView详细解析(八、在TableView实现冻结窗口)
  • Confluence高危漏洞CVE-2022-26134应急响应与安全加固实战指南
  • 把 Enterprise Services Repository 配成一座稳定的集成设计中枢
  • 洛谷 P2024:[NOI2001] 食物链 ← 扩展域并查集
  • SpringBoot 底层原理完整教程(上篇・配置文件与配置优先级)