面试被问"Java 为什么跨平台",我背过八股:编译成字节码,JVM 解释执行。但被追问一句"机器码和字节码到底什么区别,C 编译出来的 exe 为什么换台机器就跑不了"——又卡住了。这篇把三层执行模型讲透:源码、字节码、机器码各是什么,谁在执行,为什么要有中间层。
一、解决什么问题:代码是怎么"活"起来的
你写的代码是给人看的,但CPU 只认二进制。从"人看的代码"到"CPU 执行的指令",中间隔着一层甚至两层翻译。搞清楚这个过程,很多"为什么"就通了:
- 为什么 Java 能跨平台、C 不能
- 为什么 Java 要预热(JIT)
.class文件开头的CAFEBABE是什么
二、三层模型:源码 → 字节码 → 机器码
机器码(Machine Code):CPU 的母语
CPU 直接执行的二进制指令(01001110...),由硬件架构决定:
- 绑定架构:x86 的机器码在 ARM 上跑不了(指令集不同)
- 不可读:基本无法人工阅读,反汇编也是难懂的汇编
- 编译期定死:
gcc编译出 .exe 的那一秒,指令就定死了
C/C++ 走这条路:源码 → 编译 → 机器码 → 直接交给 CPU。
字节码(Bytecode):虚拟机的中间语
面向虚拟机的指令集,不绑任何硬件:
- 平台无关:Java 字节码在 Windows、Linux、macOS 的 JVM 上都能跑
- 可反编译:
javap能看(iconst_1、iadd这种"JVM 汇编") - 运行时才定:真正变成机器码是运行时(JIT)的事
Java 走这条路:源码 → 编译 → 字节码 → JVM(翻译)→ CPU。
三、核心区别
| 维度 | 机器码 Machine Code | 字节码 Bytecode |
|---|---|---|
| 谁执行 | CPU(硬件) | 虚拟机(软件) |
| 平台绑定 | 绑死 CPU 架构 | 不绑平台 |
| 可读性 | 纯二进制,不可读 | 可反编译(javap) |
| 生成时机 | 编译期生成,永不变 | 编译期生成,运行时才翻译成机器码 |
| 换平台 | 重新编译 | 换个 JVM 就行 |
| 例子 | 01001110 10010110... | iconst_1 / iadd |
一句话:机器码是给硬件看的,字节码是给虚拟机看的;字节码最后也要变成机器码才能跑,但"变"的时机和地点(运行时、各平台的 JVM 里)就是跨平台的秘密。
四、.class 文件长什么样:CAFEBABE
所有 Java 字节码文件开头固定 4 个字节 CAFEBABE(魔数),JVM 靠它识别"这是不是合法的 .class"。实测看一眼编译出来的 Hello.class 文件头:
$ xxd Hello.class
00000000: cafe babe 0000 0041 001b ... ← cafe babe 就是魔数魔数后面的字节是版本号、常量池……整个文件就是一套 JVM 能读懂的"指令+数据"。
五、字节码怎么变成机器码:解释器 vs JIT vs AOT
字节码自己不会跑,得翻译成机器码,有三种方式:
| 方式 | 做法 | 特点 |
|---|---|---|
| 解释执行 | 逐条翻译逐条执行 | 启动快,每条都翻译,慢 |
| JIT 编译 | 热点代码整体编译成机器码缓存 | 越跑越快(HotSpot 默认,详见《JVM 是什么》) |
| AOT 编译 | 提前编译成机器码(GraalVM 原生镜像) | 启动极快,但失去部分动态性 |
实测 JIT 差距(1 亿次循环,同一个 JVM):
默认(JIT 生效):25 ms
-Xint(纯解释): 426 ms ← 差 17 倍六、各语言都在用"字节码"
不是只有 Java 有字节码——只要想跨平台,都会搞一个中间层:
| 语言 | 字节码 | 虚拟机 |
|---|---|---|
| Java / Kotlin | .class(CAFEBABE) | JVM |
| Python | .pyc | CPython 解释器 |
| C# | .NET IL | CLR |
| JavaScript → WebAssembly | .wasm | 浏览器(现代浏览器) |
小结
- 机器码:CPU 母语,二进制、绑平台、编译期定死
- 字节码:虚拟机中间语,平台无关、可反编译、运行时才翻译
- 跨平台本质:字节码 + 各平台的虚拟机 = 处处可跑
- CAFEBABE:.class 文件的魔数,JVM 认它的身份证
- 三种翻译:解释器(慢)、JIT(越跑越快)、AOT(启动快)
JVM 怎么用这套机制跑起 Java 程序,看《JVM 是什么:跨平台原理与整体架构》;字节码最终在内存里怎么被管理,看《JVM 内存模型与垃圾回收》。
验证说明:
xxd查看 Hello.class 文件头cafe babe魔数、javap -c反编译字节码指令(iconst_1/iadd)、JIT 对比(默认 25ms vs-Xint426ms)全部本地实测。
