
虚拟机概述与执行流程
定义:运行 Java 字节码的抽象计算机,屏蔽底层 OS 差异,实现“一次编译,到处运行”。
执行流程:
加载:ClassLoader 加载 .class 文件至元空间。
验证:字节码安全性检查(格式、语义、操作数栈)。
执行:解释器执行 + JIT(C2/Graal)编译热点代码为本地机器码。
卸载:类不再引用时卸载元数据。
核心作用:内存自动管理(GC)、即时编译优化(JIT)、安全沙箱、跨平台。
JDK 21+ 演进:引入虚拟线程(Project Loom),大幅降低线程上下文切换开销,重塑高并发模型。
内存结构:逻辑分代与物理分区
堆(Heap):对象实例存储区,GC 核心区域。
逻辑分代(Logical Generation):
定义:根据对象存活时间划分的概念模型(新生代 Young、老年代 Old)。
实现:现代收集器(G1/ZGC/Shenandoah)中,分代是逻辑标签,而非物理隔离。
物理分区(Physical Partition):
定义:堆内存实际切分的管理单元。
规则:一个分区在同一时刻,只能属于新生代 或 老年代 中的一种(互斥性)。
动态性:分区角色可随 GC 动态变更(如 Region 从 Young 变为 Old)。
实现差异:
G1:物理单元为 Region(固定大小 1MB~32MB)。
ZGC:物理单元为 ZPage(动态大小,底层页组合)。
Shenandoah:物理单元为 Region(固定大小,类似 G1)。
元空间(Metaspace):JDK 8+ 取代永久代,使用本地内存存储类元数据、常量池。
栈(Stack):线程私有,存储栈帧(局部变量、操作数栈、动态链接)。
直接内存(Direct Memory):NIO 使用,不受堆大小限制,受本机内存限制。
程序计数器:记录当前线程执行的字节码行号。
垃圾回收算法 vs 垃圾收集器
关系:算法是理论,收集器是实现。
算法:解决“如何识别垃圾”和“如何清理内存”的逻辑(如标记、复制)。
收集器:工程组件,组合多种算法以满足吞吐量、延迟、内存约束。
算法与收集器对应:
复制算法:G1 新生代、ZGC 新生代(重定位)。
标记 - 整理:G1 老年代、ZGC 全堆(并发整理)、Shenandoah 全堆。
标记 - 清除:现代收集器已基本弃用(会产生碎片),仅特定内部阶段使用(如 CMS 老年代,已移除)。
现代主流垃圾收集器特性 G1 GC ZGC Shenandoah默认版本 JDK 9+ 默认 JDK 21+ 默认(分代) OpenJDK 发行版默认(分代 JDK 24+)分代模式 原生逻辑分代 JDK 21+ 分代默认(JDK 11-17 为单代) JDK 24+ 分代目标(JDK 12-23 主要为单代)核心算法 分区复制 + 标记整理 并发标记 + 并发整理(染色指针) 并发标记 + 并发整理(Brooks 指针)停顿时间 毫秒级(可预测) 亚毫秒级(<1ms) 亚毫秒级(<10ms)堆大小建议 < 50GB TB 级 TB 级适用场景 通用服务端,吞吐量敏感 低延迟,超大内存 低延迟,OpenJDK 生态JDK 21+ 演进 优化并行度,减少停顿 分代成为默认,吞吐量提升 4 倍 优化并发标记,适配 ARM/分代演进
CMS:已在 JDK 14 移除,不再可用。
Parallel GC:仍保留,仅推荐批处理场景(吞吐量优先,不在乎停顿)。
分代趋势:JDK 21 后,低延迟收集器全面转向“逻辑分代”以提升吞吐量。
垃圾回收核心机制
对象存活判断:
主流算法:可达性分析(Java 采用)。从 GC Roots(栈帧、静态变量、常量)搜索,不可达即垃圾。
非主流算法:引用计数(Java 堆未采用)。无法解决循环引用,仅用于 NIO 等内部模块。
标记实现技术:
双色标记:早期 Serial GC(黑/白),不支持并发。
三色标记:并发 GC 专用(白/灰/黑)。解决并发漏标,需配合屏障(写屏障/读屏障)。
指针技术:
压缩指针:所有收集器通用。64 位压缩为 32 位,节省内存(堆<32GB 自动启用)。
染色指针:ZGC 专用。指针高位存储标记状态,支持并发整理(无需对象头存标记)。
分代回收策略:
新生代(Eden + Survivor):复制算法。对象朝生夕死,复制存活对象成本低,无碎片。
老年代:标记 - 整理。对象存活率高,整理消除碎片,避免频繁复制。
大对象:
G1:直接进入 Humongous Region。
ZGC:由大 ZPage 承载(无 Humongous 概念,更灵活)。
ZGC 特殊性:
停顿与堆大小无关:停顿时间仅取决于 GC Roots 数量,而非堆中对象总数。
并发整理:通过读屏障(Load Barriers)和染色指针,在应用运行时移动对象。
物理 vs 逻辑分代本质:
传统(物理):内存地址固定隔离(如 Parallel GC),新生代满无法借用老年代空间。
现代(逻辑):内存单元标签化管理(如 G1/ZGC),空闲单元可动态借给新生代,空间利用率更高。
逃逸分析与 JIT 优化
逃逸分析:JIT 分析对象是否逃出方法/线程。
优化手段:
栈上分配:未逃逸对象直接在栈帧
配,随方法结束销毁,无 GC 压力。
标量替换:对象拆解为基本变量存储在栈帧,不创建对象头。
同步消除:线程私有对象无需锁,移除 synchronized。
JDK 21+:配合虚拟线程(Virtual Threads),进一步优化栈内存布局,减少载体线程压力,提升高并发下的内存效率。
