news 2026/6/9 18:41:36

JVM篇1:java的内存结构 + 对象分配理解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JVM篇1:java的内存结构 + 对象分配理解

JVM 内存结构 + 对象分配详解(HotSpot 虚拟机,JDK 8 ~ 21+ 主流版本)

下面内容基于 HotSpot JVM(Oracle/OpenJDK 默认实现),2025–2026 年主流生产环境基本没有大变化(PermGen 已彻底移除,Metaspace 仍是常态)。

一、JVM 运行时数据区整体划分(线程共享 vs 线程私有)

JVM 内存主要分为以下几个逻辑区域:

区域名称是否线程共享生命周期主要存储内容可能抛出的 OOM 异常是否会 GC
程序计数器 (PC Register)私有线程生命周期当前线程执行的字节码指令地址(分支、循环、异常跳转等)几乎不会 OOM
Java 虚拟机栈 (VM Stack)私有线程生命周期栈帧(局部变量表、操作数栈、动态链接、方法出口等)StackOverflowError / OOM
本地方法栈 (Native Method Stack)私有线程生命周期native 方法(JNI 调用 C/C++)的栈帧StackOverflowError / OOM
Java 堆 (Heap)共享JVM 启动 ~ 关闭几乎所有对象实例、数组OutOfMemoryError: Java heap space是(重点 GC 区域)
元空间 (Metaspace)共享JVM 启动 ~ 关闭类元数据、方法字节码、常量池、符号引用、注解等OutOfMemoryError: Metaspace是(Full GC 时可能回收)
直接内存 (Direct Memory)NIO ByteBuffer.allocateDirect() 分配的 off-heap 内存OutOfMemoryError: Direct buffer memory否(手动或 GC 间接回收)

最核心一句话记忆

二、HotSpot JVM 堆内存详细布局(分代 + G1 时代主流视图)

现代 HotSpot(JDK 8+)默认使用分代思想,但具体收集器不同,物理布局有差异。

1. 最经典的分代布局(Parallel / CMS 时代常见)
Heap ├── Young Generation(新生代) ≈ 1/3 heap │ ├── Eden(伊甸园) ≈ 8/10 young │ └── Survivor(幸存者区)×2 From / To 各 ≈ 1/10 young └── Old Generation(老年代 / 养老区) ≈ 2/3 heap
2. G1 收集器时代(JDK 9+ 默认,2025–2026 生产主流)

G1 已经取消严格的连续 Young/Old 区域,而是把整个堆切成很多个Region(默认 2048 个,可调):

G1 内存布局示意(逻辑分代,物理碎片化):

Heap → 很多个 Region(大小相等) ┌───────────────┐ │ Eden Regions │ ├───────────────┤ │ Survivor Regs │ ├───────────────┤ │ Old Regions │ ├───────────────┤ │ Humongous Reg │ ← 大对象专用 └───────────────┘

三、Java 对象创建 & 内存分配全过程(面试高频)

new 对象时 JVM 做了什么?(8 个步骤)

  1. 类加载检查
    遇到 new 指令 → 先检查常量池中是否有该类的符号引用 → 如果没有则触发类加载(加载 → 链接 → 初始化)

  2. 分配内存(核心步骤)
    在堆中划出一块确定大小的内存给对象(对象大小在类加载后已知)。

    分配方式两种(取决于堆是否规整):

    方式适用收集器堆是否规整原理并发安全解决方案
    指针碰撞Serial / ParNew指针向空闲端移动 size 距离TLAB(线程本地分配缓冲)
    空闲列表CMS / G1(部分情况)从空闲列表中找一块足够大的空间CAS + 失败重试

    TLAB(Thread Local Allocation Buffer)是 HotSpot 解决并发分配的优化:

  3. 初始化零值
    分配的内存空间(对象头除外)全部置为零值(保证字段不赋初值也能用)

  4. 设置对象头

  5. 执行 方法(构造器)
    按照代码顺序执行父类构造 → 成员变量显式赋值 → 构造代码块 → 构造方法体

四、对象分配的“特殊规则”(面试加分项)

  1. 大对象直接进老年代
    -XX:PretenureSizeThreshold=(默认 0)
    大于这个值的对象直接分配到老年代(避免在 Survivor 来回复制)

  2. 动态年龄判定(Survivor 区晋升老年代规则)
    如果 Survivor 空间中相同年龄的所有对象大小总和 > Survivor 空间的一半(默认 50%,-XX:TargetSurvivorRatio),年龄 >= 该年龄的对象直接晋升老年代

  3. 长期存活的对象进入老年代
    默认经历 15 次 Minor GC 后晋升(-XX:MaxTenuringThreshold=15)

  4. 空间分配担保
    Minor GC 前,老年代最大可用连续空间 < 新生代所有对象总大小 → 提前 Full GC

五、快速记忆口诀(面试背诵版)

内存结构口诀
“程栈本堆元,私私私共共”
程序计数器、虚拟机栈、本地方法栈(私有)
堆、元空间(共享)

堆内布局口诀(分代时代):
“新生伊甸幸存俩,老年养老养老家”

对象分配口诀
“先查类加载 → 指针碰撞或空闲列表 → TLAB 加速 → 零值填充 → 对象头 → 构造执行”

晋升老年代三板斧
大 → 直接老
老 → 年龄阈值(默认 15)
多 → 动态年龄判定(同龄 > Survivor/2)

希望这篇能让你对 JVM 内存结构 + 对象分配有清晰的整体认知。
下一期想看哪个方向?

随时告诉我!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/5 12:23:11

输入中文语音可行吗?Live Avatar语言支持测试

输入中文语音可行吗&#xff1f;Live Avatar语言支持测试 1. 引言&#xff1a;数字人交互的新可能 你有没有想过&#xff0c;对着电脑说一段中文&#xff0c;就能让一个虚拟人物实时开口说话、做出表情和动作&#xff1f;这听起来像是科幻电影里的场景&#xff0c;但随着AI技…

作者头像 李华
网站建设 2026/6/8 2:27:43

当ThreadPoolExecutor拒绝任务时,为什么选择CallerRunsPolicy能救命?

第一章&#xff1a;当ThreadPoolExecutor拒绝任务时&#xff0c;为什么选择CallerRunsPolicy能救命&#xff1f; 在高并发场景下&#xff0c;线程池是控制资源消耗的核心组件。然而&#xff0c;当线程池的任务队列已满且最大线程数达到上限时&#xff0c;新提交的任务将被拒绝。…

作者头像 李华
网站建设 2026/6/5 19:59:39

Qwen3-Embedding-0.6B怎么优化?自定义指令提升精度教程

Qwen3-Embedding-0.6B怎么优化&#xff1f;自定义指令提升精度教程 1. Qwen3-Embedding-0.6B 介绍 Qwen3 Embedding 模型系列是 Qwen 家族的最新专有模型&#xff0c;专门设计用于文本嵌入和排序任务。基于 Qwen3 系列的密集基础模型&#xff0c;它提供了各种大小&#xff08…

作者头像 李华
网站建设 2026/6/6 8:20:51

命令行长度限制引发的部署灾难,这个冷门设置救了我

第一章&#xff1a;命令行长度限制引发的部署灾难&#xff0c;这个冷门设置救了我 在一次灰度发布中&#xff0c;CI/CD 流水线突然失败&#xff0c;错误日志仅显示“Argument list too long”。排查后发现&#xff0c;问题源于构建脚本动态拼接了数千个文件路径作为命令行参数&…

作者头像 李华
网站建设 2026/6/6 7:09:04

企业级TELNET端口管理:从基础配置到安全加固

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个TELNET服务配置检查工具&#xff0c;功能包括&#xff1a;1.检查TELNET服务配置文件&#xff08;如/etc/xinetd.d/telnet&#xff09;&#xff1b;2.验证登录认证方式&…

作者头像 李华