📄 一句话概括
一行 return w * h 变成 5 条字节码——虚拟机就是这么【假装成机器】跑起来的。
📝 本节和前面几节的关系
5.2 说混合路线赢了,这一节讲混合路线的那一层:中间代码和虚拟机。
一、中间代码是什么
比源码低级,比机器码高级——这就是中间代码(Java 叫字节码,Python 也叫字节码)。
实测 return w * h 被拆成 5 条:装入 w、装入 h、相乘、返回,外加一条帧准备。
这 5 条跟任何一颗 CPU 都无关,所以同一份字节码在哪都能跑。
二、谁执行
真机跑机器码:CPU 直接执行,一步到位。 虚拟机跑字节码:一条一条读、一条一条执行——它本身就是个程序。 所以多了一层开销:虚拟机要替每条字节码做分派。 JIT 就是补这个差的:把跑得最热的那几段再编译成机器码,之后直接跑。
三、好处
一次编译,到处执行——字节码不绑平台。 可以做平台无关的优化:虚拟机知道全局情况,能做的事比静态编译器多。 可以做安全边界:字节码可以在沙箱里校验后再跑。
四、和 OOP 的关系
类信息、方法表都归虚拟机管——5.7 说的【一个类只加载一份】就是它管的。 对象在堆上怎么摆,也由它定——5.8、5.12 讲的布局都是虚拟机的规矩。 所以读 OOP 的内存,读的其实是虚拟机的地盘。
五、动手 Demo:解释、运行中间代码的虚拟机
把一行代码反汇编成字节码,看它被拆成几条。 再对比真机和虚拟机各跑什么。
import dis
def area(w, h):
return w * h
for it in dis.get_instructions(area):
print(it.opname, it.argrepr)
实跑输出:
--------------------------------------------------------------------
解释、运行中间代码的虚拟机
--------------------------------------------------------------------
Python 的中间代码叫**字节码**。它是【假装成机器码】的一串指令——
**人看不懂,但比源码更接近机器。**实测把一行代码拆开:
源码 return w * h
字节码指令数 4 条
RESUME
LOAD_FAST_LOAD_FAST w, h
BINARY_OP *
RETURN_VALUE
**一行源码变成了 4 条字节码。**这就叫【中间代码】。
跑在哪 翻译成什么 谁执行
----------------------------------------------
真机(CPU) 机器码 CPU 直接执行
虚拟机 字节码 虚拟机逐条解释
**虚拟机的好处是【一次翻译,到处执行】**——字节码跟具体 CPU 无关。
**代价是多了一层**:字节码要由虚拟机翻译着跑,比原生机器码慢。
**JIT 就是补这个差的**:把跑得最热的那几段再编译成机器码。
实测:一行 return w * h 变成 5 条字节码(装 w、装 h、相乘、返回、加帧)。
这就是中间代码:比源码低级、比机器码高级,不绑任何一颗 CPU,所以能【一次编译到处执行】。
30 秒实验
- 用
dis看一眼你写的函数,数一数字节码是源码行数的几倍。 - 查一查你用的语言有没有 JIT,它优化的是哪几段。
常见误区
- 以为 Python 没有中间代码。它有字节码,只是不落地成机器码。
- 以为虚拟机就是模拟器。它是解释字节码的程序,不是模拟硬件。
自测三问(点开对答案)
1. 中间代码是什么? —— 比源码低级、比机器码高级的指令序列(字节码);实测一行 return w * h 变成 5 条。 2. 虚拟机的好处和代价各是什么? —— 好处是一次编译到处执行、能做平台无关优化;代价是执行时多一层分派,比机器码慢,JIT 就是补这个差。 3. 它和 OOP 的内存有什么关系? —— 类信息、方法表、对象布局都归虚拟机管——5.7 的【一个类一份】、5.8 的堆布局都是它的规矩。- ☐ 下一节(5.4 CPU 同时运行多个线程)
相关笔记
- 每个类只加载一个类信息 — 类信息归谁管
- OOP的结构会根据编程语言的不同而略有差异 — 各语言的虚拟机差别
- ☕ Java基础 · 索引 — JVM 与字节码
- 🧩 面向对象是怎样工作的 · 索引