README
# 第 2 卷|运行时模型
从类到对象,从加载到执行——程序运行时究竟发生了什么?
# 🎯 这一卷要回答什么
写了多年代码,你真的理解下面这些"日常动作"吗?
- 一行
new User(),从字节码到内存中的对象,CPU 究竟做了多少事? obj.method()这句调用,编译期和运行时谁来决定该调谁?为什么 C++ 要 vtable,Java 要 itable?- 函数调用栈到底装了什么?为什么递归过深会栈溢出?尾调用为何能"无栈递归"?
- 同一份字节码为什么能跨平台运行?JVM、V8、Lua VM 的本质差异在哪?
- 为什么 JVM 服务"跑一会儿才进入状态"?JIT 究竟做了什么把性能拉起来?
class.getDeclaredField("x").set(obj, 1),反射看似"魔法",背后是不是真的违反了访问控制?- 异常机制凭什么能"穿透"多层函数?"零成本异常"的零成本到底零在哪?
第 2 卷把"运行时"从黑盒拆开,让你能在脑中画出 new 对象、调函数、抛异常的每一帧 CPU 状态。
# 📖 篇章总览(8 篇)
| 序号 | 文档 | 核心矛盾 |
|---|---|---|
| 2.1 | 类的加载核心原理 | 类加载五阶段做了什么?双亲委派为何而生? |
| 2.2 | 对象创建流程原理 | new 一个对象,从字节码到内存究竟有多少步? |
| 2.3 | 对象和函数访问原理 | 直接指针 vs 句柄、静态分派 vs 动态分派、vtable vs itable |
| 2.4 | 函数调用栈与栈帧设计 | 栈帧 / ABI / 栈展开 / 尾调用 —— 调用关系的物理痕迹 |
| 2.5 | 字节码与虚拟机执行原理 | 栈式 vs 寄存器式、解释器演进、跨平台的真相 |
| 2.6 | JIT与运行时优化 | 热点检测、内联、逃逸分析、去优化的乐观哲学 |
| 2.7 | 反射与元编程核心设计 | Java 反射 / C# Emit / JS Proxy / Python 元类的共同本质 |
| 2.8 | 异常机制设计原理 | 错误码 / 异常 / Result —— 三种错误处理范式的根本权衡 |
# 🔗 知识脉络
flowchart LR
A[2.1 类加载<br/>磁盘字节码 → 方法区] --> B[2.2 对象创建<br/>方法区 → 堆对象]
B --> C[2.3 对象访问<br/>方法调用与分派]
C --> D[2.4 调用栈<br/>函数调用的物理形态]
D --> E[2.5 字节码VM<br/>跨平台执行模型]
E --> F[2.6 JIT<br/>热点优化与去优化]
F --> G[2.7 反射<br/>运行时操作类元数据]
G --> H[2.8 异常<br/>非正常控制流]
H -.闭环.-> A
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
类加载是"准备",对象创建是"诞生",方法访问是"行动",调用栈是"骨架",字节码 VM 是"心脏",JIT 是"肌肉记忆",反射是"自我修改",异常是"应急通道"——这是程序运行时的完整画卷。
# 🌉 与其他卷的承接
- 承接第 1 卷:第 1 卷讲数据怎么被"表示",本卷讲数据怎么被"组织"成类和对象、并被运行时执行。
- 通往第 3 卷:理解了调用栈与对象布局,才能理解第 3 卷"对象 header 里 mark word 的锁状态"和"协程为什么需要自己的栈"。
- 通往第 4 卷:堆里的对象会被 GC 管理,本卷的对象布局是第 4 卷 GC 算法的前置知识。
# 💡 学完你能回答
- 为什么 Spring 启动 5s 中的 4s 都在做类加载?怎么优化?
new在 JVM 里是不是原子的?多线程并发 new 会不会出问题?- 一次普通函数调用,CPU 实际执行了多少条指令?
- 为什么解释执行慢?JIT 把哪些"假设"变成了"事实"?
- 反射真的慢吗?慢在哪?JIT 能优化掉多少?
- Hot Reload、热修复、字节码增强(ASM、Javassist),它们工作的原理是不是同一个?
- 异常路径为什么比正常路径慢上百倍?
# 🛣 推荐阅读顺序
2.1 类加载("准备")
↓
2.2 对象创建("诞生")
↓
2.3 方法访问("行动")
↓
2.4 调用栈("骨架")
↓
2.5 字节码 VM("心脏")
↓
2.6 JIT("肌肉记忆")
↓
2.7 反射("自我修改")
↓
2.8 异常("应急通道")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上次更新: 2026/06/07, 10:26:12