约 5204 字
JVM完整原理(JDK8~25)内存结构、分代模型、GC算法、垃圾收集器、元空间、双亲委派机制
本文覆盖JVM整体结构、运行时数据区、JDK8内存大改版、分代模型、四大GC核心算法、主流垃圾收集器、元空间原理、双亲委派机制+破双亲委派,适配JDK8至最新JDK25版本特性,全部为一线面试高频原题,可直接背诵。
一、JVM整体架构(四大核心模块)
Java虚拟机是跨平台执行引擎,整体分为四大核心模块,所有Java代码运行都依赖该体系,整体结构如下:
JVM整体架构
├─ 类加载子系统 → 加载、验证、准备、解析、初始化(双亲委派)
├─ 运行时数据区 → 线程私有+线程共享内存区域
├─ 执行引擎 → 解释器 + JIT即时编译 + 本地方法执行
└─ GC垃圾回收子系统 → 内存分代、GC算法、垃圾收集器、内存回收- 类加载子系统:加载class文件、验证、准备、解析、初始化,双亲委派机制核心载体
- 运行时数据区:内存模型(堆、栈、元空间等),线程私有/共享内存分区
- 执行引擎:解释器、JIT即时编译器、本地方法栈,执行字节码指令
- 垃圾回收子系统:内存分代、GC算法、垃圾收集器、内存回收、内存优化
二、JVM运行时数据区(JDK8+ 最终版内存结构)
JVM运行时数据区(JDK8+)
├─ 线程私有(线程销毁自动释放)
│ ├─ 程序计数器(无OOM)
│ ├─ 虚拟机栈(栈帧、方法执行)
│ └─ 本地方法栈(Native方法执行)
└─ 线程共享(全局常驻、GC管理)
├─ 堆 Heap(对象/数组、GC主战场)
└─ 元空间 Metaspace(本地内存、存类元数据)1. 线程私有(线程隔离、线程销毁自动释放)
- 程序计数器 PC寄存器:唯一无OOM的区域,记录当前线程执行字节码行号,多线程切换恢复现场
- 虚拟机栈 JVM Stack:存储栈帧(局部变量表、操作数栈、动态链接、方法出口),方法调用入栈、执行完出栈,栈深度溢出报StackOverflowError
- 本地方法栈 Native Stack:执行native本地方法(C/C++),底层线程、IO、CAS依赖此区域
2. 线程共享(全局共享、GC管理核心)
- 堆 Heap:所有对象、数组实例存储区域,GC唯一主战场,分年轻代、老年代
- 元空间 Metaspace(JDK8全新):替代永久代,存储类元数据、方法信息、常量池,使用本地内存,无固定大小限制
3. JDK8 史诗级内存改动(必考)
- JDK7及以前:存在永久代 PermGen,堆内存一部分,固定大小,极易OOM
- JDK8 彻底移除永久代,替换为元空间 Metaspace
- 字符串常量池、静态变量从永久代迁移至堆内存
- 元空间使用操作系统本地内存,不再占用堆内存,默认动态扩容,大幅降低OOM概率
三、JVM分代模型(JDK8~25 统一分代)
为什么需要分代?不同对象生命周期不同,针对性GC,提升回收效率。90%对象朝生夕灭,只需频繁回收新生代,老年代低频回收。
堆内存分代结构
├─ 年轻代(占堆1/3)
│ ├─ Eden区(80%)→ 新对象默认存放
│ ├─ Survivor0(10%)
│ └─ Survivor1(10%)→ S0/S1始终一空一用
└─ 老年代(占堆2/3)→ 长期存活对象、大对象、晋升对象
对象晋升链路:Eden → Survivor循环存活15次 → 老年代为什么需要分代?不同对象生命周期不同,针对性GC,提升回收效率。90%对象朝生夕灭,只需频繁回收新生代,老年代低频回收。
1. 年轻代(Young Generation)
- 占堆内存 1/3,分为 Eden区 + Survive0 + Survive1
- 比例默认:8:1:1
- Eden:新建对象默认存放区域
- S0/S1:存活对象复制区,始终空出一块作为备份
- GC类型:Minor GC,频繁执行、速度快
2. 老年代(Old Generation)
- 占堆内存 2/3
- 存放长期存活对象、大对象、多次GC存活对象
- GC类型:Major GC / Full GC,耗时久、频率低、尽量避免
3. 对象晋升机制
- 对象在新生代经历 15次Minor GC存活,晋升老年代(可通过JVM参数调整)
- 大对象直接进入老年代,避免新生代复制开销
- 新生代空间不足,存活对象提前晋升老年代
四、四大GC核心算法(底层原理)
GC算法适配结构图
├─ 标记清除:标记存活 → 清空死亡 → 产生内存碎片
├─ 标记复制:标记存活 → 复制新区域 → 清空原区域(无碎片、耗空间)
├─ 标记整理:标记存活 → 压缩规整 → 清空末端(无碎片、速度慢)
└─ 分代收集(综合最优)
├─ 年轻代 → 标记复制算法
└─ 老年代 → 标记清除 + 标记整理算法1. 标记清除算法(Mark-Sweep)
- 流程:标记存活对象 → 清除死亡对象
- 优点:无需移动对象,速度较快
- 缺点:产生大量内存碎片,大对象无法分配,容易触发Full GC
2. 标记复制算法(Mark-Copy)
- 流程:标记存活对象 → 复制到空闲区域 → 清空原区域
- 优点:无内存碎片,内存规整
- 缺点:浪费一半内存空间,适合存活对象少的区域
- 适用:年轻代默认算法(存活对象少、回收频繁)
3. 标记整理算法(Mark-Compact)
- 流程:标记存活对象 → 向一端整理压缩 → 清空末端内存
- 优点:无内存碎片、不浪费空间
- 缺点:需要移动对象,效率低
- 适用:老年代默认算法(存活对象多、不适合复制)
4. 分代收集算法(GC综合算法)
- JVM终极算法,整合以上三种
- 年轻代:标记复制
- 老年代:标记清除 + 标记整理
五、主流垃圾收集器(JDK8~25 版本迭代)
垃圾收集器迭代演进 & 特性
├─ Parallel GC(JDK8默认)→ 高吞吐、长停顿、后台批量任务
├─ CMS(JDK8废弃/JDK14移除)→ 低并发停顿、碎片多、浮动垃圾
├─ G1 GC(JDK9默认)→ 分区回收、吞吐延迟平衡、大堆适配
└─ ZGC/Shenandoah(JDK11+)→ 毫秒级低延迟、超大堆、高并发首选1. Parallel GC(JDK8 默认)
- 多线程并行回收,追求高吞吐量
- 适合后台业务、批量任务
- 停顿时间较长,不适合低延迟服务
2. CMS 并发收集器(JDK8废弃、JDK14移除)
- 追求低停顿,并发回收
- 缺点:内存碎片多、并发浮动垃圾、CPU占用高
3. G1 GC(JDK9默认,JDK8可指定)
- 分区回收,将堆划分为多个Region
- 兼顾吞吐量与低延迟,可设置最大停顿时间
- 适用于大内存堆服务
4. ZGC / Shenandoah(JDK11+ 超低延迟)
- 毫秒级停顿,几乎不卡顿
- 超大堆适配,JDK17、JDK25持续优化
- 互联网高并发、低延迟服务首选
5. GC迭代总结(版本演进)
ParallelGC(吞吐) → CMS(并发低延迟) → G1(平衡) → ZGC(极致低延迟)
六、元空间 Metaspace 深度原理(JDK8核心)
JDK8内存重大更替
永久代PermGen(JDK7及以前)
├─ 归属:堆内存一部分
├─ 特点:固定大小、容量小、极易OOM
└─ 存储:类元数据、常量池、静态变量
元空间Metaspace(JDK8+ 全覆盖)
├─ 归属:操作系统本地内存
├─ 特点:动态扩容、无固定上限、无OOM风险
└─ 存储:类/方法/字段元数据、运行时动态类信息1. 永久代与元空间核心区别
- 永久代PermGen:属于堆内存、固定大小、易OOM、JDK7及以前使用
- 元空间Metaspace:操作系统本地内存、动态扩容、无固定上限、JDK8+全覆盖
2. 元空间存储内容
- 类、接口、注解、枚举的元数据信息
- 方法代码、字段信息、常量池
- 运行时动态生成的类(Spring CGLIB、动态代理)
3. 元空间优势
- 彻底解决永久代OOM问题
- 本地内存不占用堆空间,堆内存利用率更高
- 动态伸缩,适配动态类加载场景
七、双亲委派机制(类加载核心)
类加载器层级 & 双亲委派流程
加载层级(从上至下)
├─ BootstrapClassLoader 启动类加载器(顶层)
├─ ExtClassLoader 扩展类加载器
└─ AppClassLoader 应用类加载器(底层)
执行流程:向上委托,向下加载
1. AppClassLoader 接收加载请求
2. 逐级向上委托至 Bootstrap
3. 上级无法加载,逐级下放由本级加载1. 三大类加载器(层级结构)
- 启动类加载器 BootstrapClassLoader:顶层,加载JDK核心rt.jar等系统类
- 扩展类加载器 ExtClassLoader:加载JDK扩展包类
- 应用类加载器 AppClassLoader:加载项目自定义class、第三方依赖包
2. 双亲委派核心流程(必背)
向上委托、向下加载
- 应用类加载器收到加载请求,不直接加载,向上委托给扩展类加载器
- 扩展类加载器继续向上委托给启动类加载器
- 启动类加载器无法加载,向下逐层返回,由下层加载器加载
3. 核心作用(面试满分答案)
- 沙箱安全机制:防止恶意代码覆盖JDK核心类(如自定义java.lang.String)
- 类全局唯一:保证核心类只加载一次,避免重复加载、类冲突
4. 打破双亲委派机制(高频追问)
双亲委派是默认规则,非强制机制,可重写loadClass方法打破
经典打破场景
- Tomcat:自定义类加载器,隔离不同web应用包冲突
- JDBC SPI机制:线程上下文类加载器,加载厂商实现类
- Spring、OSGI:热部署、动态加载类,打破双亲委派
八、GC核心概念区分(面试坑点)
- Minor GC:新生代GC,频繁、速度快、无停顿或短停顿
- Major GC:老年代GC,耗时较长
- Full GC:整堆GC(新生代+老年代+元空间),STW全局暂停,线上绝对避免
- STW:Stop-The-World,GC期间暂停所有用户线程,是卡顿的根本原因
九、终极面试背诵总结(极简版)
- JVM结构:类加载、运行时数据区、执行引擎、GC四大模块
- 内存分区:线程三私两共,JDK8废除永久代,改用本地内存元空间
- 分代模型:新生代8:1:1复制算法,老年代标记整理算法,对象随GC次数晋升
- GC算法:清除有碎片、复制耗空间、整理无碎片效率低,分代收集综合最优
- 收集器迭代:Parallel吞吐、G1平衡、ZGC超低延迟
- 双亲委派:向上委托保证安全和类唯一,Tomcat/SPI可打破实现隔离与动态加载