前言
写 Java 时,我们很容易把下面这段代码当成理所当然:
int a = 2;
int b = 3;
int c = a + b;
但如果继续往下追,就会出现一串很有意思的问题:
- CPU 并不认识 Java,那它到底认识什么?
- 机器为什么偏偏使用 0 和 1,而不是十进制?
- 晶体管只是一个极小的电子器件,它怎么一步步变成加法器、寄存器,最后又变成 CPU?
- 一条
a + b,最后究竟经历了 Java 字节码、机器指令、寄存器和运算单元中的哪些步骤? - CPU 的 3GHz、4GHz 到底意味着什么?是不是一个时钟周期就执行一条指令?
- “汇编语言就是机器语言”“Java 是解释执行”这些常见说法,到底准确到什么程度?
如果这些问题没有串起来,CPU、寄存器、汇编、机器码、JVM 往往只是几个互相孤立的名词。
这篇文章不准备深入半导体工程,也不会去背计算机发展史上的人物和年份,而是只做一件事:把一条从物理材料一直延伸到 Java 程序执行的链路真正串起来。
硅材料
↓
晶体管
↓
逻辑门
↓
组合/时序电路
↓
CPU
↓
机器指令
↓
JVM
↓
Java 程序
理解这条链路之后,很多“底层知识”就不再是零散的名词。
一、从 0 和 1 到晶体管
1. 机器如何表示数字
我们习惯写:
0
1
2
3
4
5
...
但数字本身只是抽象概念。
机械装置、电路和芯片并不天然理解“2”是什么意思。任何计算设备首先都必须解决一个问题:
怎样用一种稳定、可区分、可重复读取的物理状态表示信息?
早期机械计算装置可以用齿轮位置表示不同数字;电子计算机则更适合利用电路状态。
现代数字电路通常把一定范围内的电压解释成逻辑低电平或逻辑高电平,再把这两类逻辑状态抽象成:
0
1
这里需要注意,真实芯片里并不是简单的“完全没电 = 0,有电 = 1”。
电路真正识别的是不同的电压范围和逻辑电平。0 与 1 是我们在数字逻辑层面对物理状态做出的抽象。
1.1 为什么二进制特别适合电子计算机?
理论上当然可以设计十进制计算机,但二进制有一个非常现实的工程优势:
只需要可靠地区分两种状态。
如果一个器件要直接表示十进制,它必须稳定地区分十种状态;而二进制只需要区分两种。
这会让电路设计、噪声容忍、信号恢复和逻辑组合都容易得多。
所以现代计算机最重要的一层抽象就是:
物理电压状态
↓
逻辑 0 / 1
↓
bit
↓
更复杂的数据
一个 bit 只能表示两个状态,多个 bit 组合后就能表示越来越大的范围:
1 bit → 2 种状态
2 bit → 4 种状态
3 bit → 8 种状态
n bit → 2^n 种状态
整数、字符、图片、声音,最终都可以编码成 bit 序列。
2. 从机械计算到晶体管
没必要把计算机发展史写成人名和年份大全。对于理解现代 CPU,更有价值的是看清楚它一直在解决什么问题。
2.1 机械计算:用机械结构表示数字
最早的自动计算可以通过齿轮、转轮等机械结构表示数字。
它证明了一件很重要的事情:
计算不一定需要人脑亲自完成,只要能设计出一套状态和状态变化规则,机器就可以按规则运算。
但纯机械结构体积大、速度慢、磨损严重,很难继续提升复杂度。
2.2 电子管:把机械状态变成电状态
电子管时代开始使用电信号完成开关和放大。
相比复杂的机械运动,电子信号变化快得多,于是计算速度有了巨大提升。
但电子管也有明显问题:
- 体积大;
- 功耗和发热高;
- 器件寿命和可靠性有限;
- 当系统需要成千上万个器件时,维护成本非常高。
真正改变现代计算机形态的,是后来大规模使用半导体晶体管。
2.3 晶体管:让电子开关变得极小
可以先把晶体管理解成一种可以被电压控制的电子开关。
现代处理器使用的核心器件主要是 MOSFET 一类场效应晶体管。它可以利用控制端的电压影响导电状态,于是非常适合构造数字逻辑。
相比电子管,晶体管可以做到更小、更低功耗、更高可靠性,而且能够大规模集成。
接下来事情就发生了质变:
一个晶体管
↓
很多晶体管
↓
逻辑门
↓
加法器 / 选择器 / 锁存器
↓
寄存器 / 运算单元 / 控制逻辑
↓
CPU
现代处理器之所以能够完成极其复杂的工作,本质上不是因为它突然获得了某种“智能”,而是因为我们把数量庞大的简单开关组织成了越来越复杂的逻辑网络。
3. 从硅到 CPU
对于软件开发者,没有必要深入光刻胶配方、离子注入能量、晶圆缺陷检测等半导体制造细节,但“从什么东西变成 CPU”这条链路值得知道。
3.1 “CPU 是沙子做的”为什么只是简化说法?
经常有人说:
CPU 是沙子做出来的。
这句话可以建立直觉,但不够严谨。
常见沙子中含有大量二氧化硅 SiO₂,芯片制造需要的是经过提取和高度提纯的硅,再进一步形成高质量单晶硅材料。
所以更合理的认知链路是:
3.2 晶圆怎样一步步变成芯片?
芯片制造最容易产生的误解,是把光刻想成:
拿一块硅片
↓
刻一下
↓
CPU 完成
真实过程远比这个复杂。
在晶圆上制造晶体管和多层互连,需要反复进行许多类型的工艺,例如:
薄膜形成
↓
涂覆光刻胶
↓
光刻曝光
↓
显影
↓
刻蚀
↓
掺杂 / 离子注入
↓
沉积
↓
平坦化
↓
继续下一层
这些步骤会不断重复,逐渐构造出晶体管以及连接晶体管的多层金属互连。
一个晶圆上通常同时制造大量芯片,完成后再进行测试、切割,得到一个个独立的 Die,最后封装成我们实际安装在主板上的处理器。
因此,“从硅到 CPU”真正想建立的不是半导体工程知识,而是一个认知:
程序最终运行的物理基础,是被制造在硅晶圆上的海量晶体管和互连电路。
4. 从晶体管到逻辑电路
知道晶体管可以充当开关之后,还差一个最关键的问题:
一堆开关为什么能做加法?
答案是逻辑门。
4.1 基础逻辑门
常见基础逻辑可以抽象为:
| A | B | AND | OR | XOR |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
此外还有 NOT:
NOT 0 = 1
NOT 1 = 0
晶体管按照不同拓扑组合以后,就能实现这些逻辑关系。
4.2 逻辑门怎样组成加法器?
这是一个很容易出现的简化。
布尔代数里经常把 OR 写成类似“逻辑加”的形式,但算术加法并不等于一个 OR 门。
例如:
1 OR 1 = 1
而二进制算术:
1 + 1 = 10
这里已经出现了进位。
最简单的半加器会同时产生两个结果:
Sum = A XOR B
Carry = A AND B
也就是说:
继续把多个门组合起来,可以形成全加器;再把多个加法器连接,就能处理多位二进制数。
同样的思想还可以继续构造:
- 比较器;
- 选择器;
- 移位器;
- 锁存器;
- 寄存器;
- 算术逻辑单元。
CPU 的复杂,本质上来自大量简单逻辑电路的层层组合。
二、认识现代计算机与 CPU
1. CPU 与其他硬件怎样协作?
理解 CPU 之前,先把整台计算机缩成一个最简单的模型。
这个图不是在描述某一款主板的真实走线,而是在表达逻辑关系:
- 程序长期保存在 SSD 或硬盘中;
- 真正运行时,代码和数据会进入内存;
- CPU 从内存层次中获得指令和数据;
- GPU、网卡、磁盘控制器等设备通过现代片上互连、PCIe、内存控制器等机制与处理器和内存系统协作。
1.1 “总线就是 64 根线”为什么只是入门比喻?
传统教材常把总线分为:
地址总线
数据总线
控制总线
这个模型非常适合入门。
但现代处理器内部和主板上的互连已经复杂得多,不一定是一组所有设备共同挂载的简单并行导线。
同样,“64 位 CPU”也不能简单理解为:
CPU 外面一定有 64 根数据线。
在 x86-64 这类架构中,“64 位”首先涉及指令集架构、通用寄存器宽度、地址能力等概念。某条内部或外部总线究竟多宽,是另一个具体的硬件设计问题。
所以“64 根线”适合帮助第一次理解并行传输,但不要把它当成现代 CPU 的严格物理定义。
2. CPU 核心的关键部件
为了理解程序执行,不需要一开始就研究真实芯片上数十亿个晶体管的布线。
先保留下面几个最重要的功能模块:
先用一句话认识它们:
| 中文名称 | English Name | 缩写 | 最核心的职责 |
|---|---|---|---|
| 程序计数器 | Program Counter | PC | 保存当前控制流执行到哪里 |
| 寄存器 | Register | — | 保存 CPU 当前正在快速使用的数据、地址和中间结果 |
| 算术逻辑单元 | Arithmetic Logic Unit | ALU | 完成整数算术、逻辑、比较、移位等运算 |
| 控制单元 | Control Unit | CU | 组织取指、译码和执行等控制过程 |
| 内存管理单元 | Memory Management Unit | MMU | 参与虚拟地址到物理地址的转换和访问权限检查 |
下面一个一个看。
这里仍然是一张为了建立整体认知而画的功能模型,不是现代 CPU Core 的真实物理布局。现代处理器内部还有分支预测器、译码器、调度器、重排序缓冲区、Load/Store 单元、向量执行单元、TLB 等大量结构,后面涉及具体执行过程时再继续打开这一层。
2.1 程序计数器 PC
很多体系结构都有“Instruction Pointer / Program Counter”这一类概念,用来描述当前或下一条指令的位置。
在不同 ISA 中,它的具体名称不同。
例如在现代 x86-64 中,最直接对应的是 RIP。早期 x86 中则有 IP、EIP,并与分段机制存在历史关系。
因此,把所有 Intel CPU 的 PC 都直接说成 CS:IP 并不准确,那更接近早期 x86 实模式下的表达方式。
从概念上理解即可:
指令地址
↓
取出指令
↓
执行
↓
更新到后续指令地址
当然,现代 CPU 有分支预测、流水线和乱序执行,真实硬件并不是每执行完一条才老老实实取下一条。
但从体系结构可见语义上看,程序必须始终能够确定“控制流接下来去哪里”。
JVM 的 PC 与物理 CPU 的 RIP
JVM 规范中每个线程也有自己的 PC Register。
它描述的是 JVM 指令执行位置,是虚拟机层面的运行时概念,不是 Java 线程直接占有了 CPU 的物理 RIP 寄存器。
这两个概念长得很像,是因为 JVM 本身就在抽象一台执行字节码的虚拟机器。
2.2 寄存器 Register
如果 ALU 每做一次计算都跑到内存取数据,CPU 会把大量时间浪费在等待上。
所以 CPU 内部有寄存器,用来保存当前正在处理的数据、地址和状态。
以 x86-64 为例,经常能看到:
RAX
RBX
RCX
RDX
RSP
RBP
RSI
RDI
...
寄存器是理解汇编语言的关键,因为大量机器指令都会显式或隐式操作寄存器。
2.3 算术逻辑单元 ALU
ALU 是 Arithmetic Logic Unit,也就是算术逻辑单元。
一个非常简化的过程可以写成:
内存 / Cache 中的数据
↓
寄存器
↓
ALU
↓
寄存器
↓
继续计算或写回
但现代 CPU 并不是只有“一个 ALU”。
实际处理器往往存在多种执行资源,例如:
- 整数执行单元;
- 浮点 / 向量执行单元;
- Load / Store 单元;
- 分支执行相关单元;
- SIMD / 矩阵等扩展执行资源。
所以“CPU = 一个 ALU + 几组寄存器”只能作为非常粗的教学模型。
2.4 控制单元 CU
传统框图里经常有一个 CU,也就是 Control Unit。
它可以帮助理解 CPU 需要完成:
取指
↓
理解指令
↓
产生控制信号
↓
调度执行资源
↓
管理结果
在现代高性能 CPU 里,这些功能已经分散到前端、译码器、调度器、重排序缓冲区、分支预测器等大量结构中,所以不必强行寻找一块物理上独立的“CU 芯片”。
2.5 内存管理单元 MMU
MMU 是 Memory Management Unit。
现代操作系统不会简单地让普通进程直接拿物理内存地址读写。程序看到的是虚拟地址空间,地址转换要依赖操作系统建立的页表以及 CPU 中的 MMU、TLB 等硬件配合。
这一部分会进入虚拟内存、页表和操作系统内存管理,适合单独展开。这里只需要先知道:
CPU 执行 Load / Store 时,地址并不总是可以直接当成物理内存地址使用。
3. 主频、时钟周期与指令执行
经常能看到:
3.5 GHz
4.0 GHz
5.0 GHz
GHz 表示每秒十亿级的时钟周期。
时钟信号可以理解为同步数字电路运行节奏的重要基准。
但有一个特别容易产生的误区:
一个时钟周期 = 执行一条指令。
现代 CPU 不能这么理解。
3.1 指令延迟与吞吐量
一条指令从进入流水线到结果真正可用,可能需要多个周期。
但与此同时,CPU 可以让多条不同指令的不同阶段重叠执行。
就像工厂流水线:
周期 1:指令 A 取指
周期 2:指令 A 译码,同时指令 B 取指
周期 3:指令 A 执行,同时 B 译码,同时 C 取指
...
进一步再加上:
- 超标量;
- 乱序执行;
- 分支预测;
- 寄存器重命名;
- 多执行端口;
现代 CPU 一个周期可能开始执行多项工作,也可能因为缓存未命中、数据依赖等原因停顿很多周期。
因此:
主频决定时钟节奏,但“GHz × 某个固定数字 = 每秒指令数”并不成立。
4. 超线程与逻辑处理器
“4 核 8 线程”经常让人产生一种错觉:
4 个核心通过超线程就变成了 8 个核心。
不是。
Intel Hyper-Threading 属于 SMT(Simultaneous Multithreading,同时多线程)思想的一种实现。
一个支持 HT 的物理核心可以向操作系统暴露多个逻辑处理器。每个逻辑处理器拥有自己的一部分体系结构状态,例如通用寄存器和控制寄存器;但很多真正昂贵的执行资源仍然是共享的,例如部分执行单元、Cache 和内部总线资源。
可以把它理解成:
Physical Core
┌───────────────┐
Thread0 │ Architectural │
│ State 0 │
├───────────────┤
Thread1 │ Architectural │
│ State 1 │
├───────────────┤
│ Shared Cache │
│ Execution │
│ Resources │
└───────────────┘
它的价值在于:
当一个线程因为数据依赖、缓存等待等原因不能充分利用核心时,另一个线程可能利用空闲执行资源,提高整个物理核心的利用率。
所以:
4C8T ≠ 8 个物理核心
它也不会自动带来 2 倍性能。
最终提升多少,要看工作负载是否真的能互补地利用共享资源。
三、从机器码到 Java 字节码
这三个东西很容易混在一起。
1. 机器码:CPU 真正执行的指令
以 x86-64 为例,处理器按照 x86-64 ISA 规定的格式读取机器指令。
机器码本质上就是一串二进制编码。
人当然也可以把它写成十六进制:
48 89 E5 ...
但直接用这些编码写大型程序几乎不可维护。
2. 汇编语言:机器指令的助记符
于是出现了:
mov
add
sub
cmp
jmp
这些是助记符。
真正的过程是:
因此,说:
汇编语言和机器语言完全一样,CPU 直接执行汇编。
并不严谨。
CPU 不认识文本里的 mov、add 字符串,汇编器要先把它们编码成目标 ISA 的机器指令。
另外,也不能把“一个汇编源语句一定对应一条机器指令”当成绝对规律。伪指令、宏、汇编器指令等都可能打破这种简单的一一对应。
3. JVM 字节码:虚拟机指令集
Java 编译器执行:
javac Calc.java
之后得到:
Calc.class
Class 文件里面包含的是 JVM 字节码,而不是 x86-64 或 ARM64 的本地机器码。
所以:
JVM Bytecode ≠ x86 Machine Code
JVM Bytecode ≠ ARM64 Machine Code
JVM 规范定义了一套虚拟机指令,例如:
iload
istore
iadd
invokevirtual
return
这可以把 JVM 理解成一台“抽象计算机”。
4. Java 的解释执行与 JIT
常见说法是:
C/C++ 是编译执行,Java 是解释执行。
这个说法适合入门,但对现代 HotSpot 来说太粗糙。
HotSpot 通常会先解释执行代码,同时收集运行时信息;当某些方法或代码路径变成热点后,JIT 编译器会把这些热点代码编译成本地机器码,并进一步做内联、逃逸分析等优化。
更准确的流程是:
所以 Java 的跨平台关键不是“CPU 能执行 Java”,而是:
同一份符合 JVM 规范的 Class 文件,可以交给不同平台上的 JVM 实现,再由 JVM 适配到本地 ISA。
四、实验:一段 Java 加法如何运行
现在真正跑一个最简单的实验。
测试环境:
OpenJDK 21.0.11
代码:
public class Calc {
public static void main(String[] args) {
int a = 2;
int b = 3;
int c = a + b;
System.out.println(c);
}
}
1. 编译 Java 源代码
执行:
javac Calc.java
这一步的目的不是生成 CPU 机器码,而是生成:
Calc.class
也就是 JVM Class 文件。
2. 用 javap 查看字节码
执行:
javap -c -p Calc.class
参数含义:
-c 反汇编方法中的 JVM 字节码
-p 显示所有类和成员
在 OpenJDK 21.0.11 的测试环境中,main 方法得到:
public static void main(java.lang.String[]);
Code:
0: iconst_2
1: istore_1
2: iconst_3
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_3
8: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_3
12: invokevirtual #13 // Method java/io/PrintStream.println:(I)V
15: return
不同 JDK / 编译器版本下常量池编号等细节可能不同,但这段简单代码的核心字节码结构很直观。
3. 分析 a + b 的字节码
先看:
0: iconst_2
1: istore_1
意思可以粗略理解成:
把常量 2 压入操作数栈
↓
弹出并存入局部变量槽 1
接着:
2: iconst_3
3: istore_2
处理 b = 3。
真正的:
int c = a + b;
对应:
4: iload_1
5: iload_2
6: iadd
7: istore_3
也就是:
最后:
getstatic
iload_3
invokevirtual
获取 System.out,读取 c,调用 println(int)。
4. 从 iadd 到 CPU 机器指令
不是。
这是全文非常重要的一层区别:
Java 源代码里的 +
↓
JVM 指令 iadd
↓
解释器 / JIT
↓
目标平台机器指令
↓
CPU 执行
iadd 属于 JVM 指令集。
最终在 x86-64、ARM64 等不同平台上,JIT 可能生成不同的机器指令序列,而且经过优化后也不保证存在一个机械的一一映射关系。
五、程序最终怎样在 CPU 上执行
1. 本地程序是怎样装载的?
有一种常见说法是:
双击 EXE 后,操作系统把程序加载到内存,并把它转换成机器码。
这句话把“装载”和“编译”混在一起了。
对于已经编译好的本地可执行程序,例如某个 x86-64 ELF 或 PE 文件,其中本来就已经包含面向目标 ISA 的机器代码。
操作系统加载器主要做的是类似:
读取可执行文件格式
↓
建立进程虚拟地址空间
↓
映射代码段 / 数据段
↓
处理必要的重定位
↓
装载和链接动态库
↓
准备栈、参数和运行环境
↓
把控制权交给入口点
它不是等到程序启动时才把 C/C++ 源代码重新翻译成机器码。
Java 的情况不同,因为 .class 里面不是本地 ISA 机器码,而是 JVM 字节码。
2. CPU 的指令执行流程
最经典的模型是:
Fetch
↓
Decode
↓
Execute
↓
Write Back
这个模型适合建立第一层认知,但现代 CPU 已经复杂得多。
更接近现代处理器的抽象流程可以写成:
2.1 Fetch:取指
CPU 前端根据控制流预测,从指令 Cache 等层级获得机器指令字节。
2.2 Decode:译码
x86 这类复杂指令集处理器通常会把机器指令进一步译码成内部可以调度的微操作。
2.3 Rename / Schedule:重命名与调度
现代 CPU 为了减少不必要的数据依赖,会进行寄存器重命名。
之后调度器会根据操作数是否已经准备好,把微操作送往合适的执行端口。
2.4 Execute:执行
整数 ALU、Load/Store、向量单元等执行资源开始工作。
2.5 Retire:提交结果
虽然内部可能乱序执行,但最终需要保证程序对外可观察的体系结构状态符合规则。
这就是为什么:
现代 CPU 内部可以非常“乱”,但程序不能因此随机算错。
乱序执行、分支预测、内存屏障等问题,已经进入另一个更大的主题。
3. CPU、GPU 与专用计算
CPU 追求的是通用性。
它要处理:
- 操作系统;
- 分支很多的业务逻辑;
- 中断;
- 各种不同类型程序;
- 单线程低延迟任务。
GPU 则更偏向吞吐量设计,拥有大量适合并行执行相似运算的计算资源,并配合高带宽内存系统。
很多机器学习工作负载恰好包含大量:
矩阵乘法
向量运算
张量运算
所以 GPU 很适合这类高度并行的工作。
进一步还会出现各种专用 AI 加速器。
但这也不能简化成:
AI 芯片一定比 GPU 快,GPU 一定比 CPU 快。
性能永远依赖具体算法、数据规模、精度、内存访问模式以及硬件实现。
六、扩展:量子计算会改变什么?
量子计算不是现代 CPU 的“下一代升级版”,它使用的是另一套计算模型。
传统 bit 在一次测量意义上是:
0 或 1
一个 qubit 的量子态则可以写成:
|ψ⟩ = α|0⟩ + β|1⟩
其中 α、β 是概率幅,并满足归一化条件。
1. 量子并行到底是什么意思?
这是非常流行的说法,但会产生误解。
量子态确实可以存在多个基态分量,但测量时并不会把 2^n 个结果一次性全部吐给程序。
量子算法真正利用的是:
- 叠加;
- 干涉;
- 纠缠;
- 特定算法结构;
让我们想要的结果概率被增强,不想要的结果概率被削弱。
所以量子计算并不是一个“把暴力搜索速度变成无限快”的万能机器。
2. 量子计算为什么会影响密码学?
Shor 算法意味着:
如果未来出现足够大、足够稳定的容错量子计算机,RSA、ECC 等依赖特定数学困难问题的公钥密码体系会受到严重威胁。
这也不是“未来再考虑”的事情。NIST 已经发布首批后量子密码标准,软件和基础设施正在逐步准备迁移。
3. Java 未来会怎样调用量子计算?
不一定。
一种完全可能的未来形态反而更像今天的 GPU:
绝大多数控制逻辑仍然由经典计算机完成,只有适合量子算法的问题被下发给 QPU。
也就是说,未来真正可能发生的不是:
CPU 全部被量子计算机替代。
而更可能是:
经典 CPU、GPU、NPU、QPU 各自处理最适合自己的计算。
当然,这一部分属于基于当前技术路线的推演,而不是已经确定的未来。
七、常见误区
1. 数字电路相关误区
误区 1:CPU 只认识 0 和 1,所以里面真的只有“通电”和“断电”
不准确。
0 和 1 是数字逻辑抽象,底层由一定范围的电压、电荷状态以及复杂器件行为实现。
误区 2:二极管升级一下就变成了 CPU 的晶体管
不准确。
二极管、MOSFET 等是不同的半导体器件结构。理解 CPU 时,只需要抓住“晶体管可以构成受控开关,再组合出逻辑门”这一主线,不需要把它们硬连成一个简单的升级链。
误区 3:OR 门就是加法器
错误。
OR 是逻辑或。真正的二进制加法还要处理进位,半加器至少会使用 XOR 和 AND。
2. 指令与运行时相关误区
误区 4:汇编语言就是 CPU 直接执行的机器语言
不准确。
汇编源代码要先经过汇编器编码成机器指令,CPU 执行的是机器指令编码。
误区 5:Java 是纯解释执行
不准确。
现代 HotSpot 会解释执行并收集运行信息,再通过 JIT 把热点代码编译成本地机器码。
3. CPU 并行与性能相关误区
误区 6:4 核 8 线程就是 8 个核心
错误。
SMT / Hyper-Threading 暴露多个逻辑处理器,但执行单元、Cache 等大量物理资源依然共享。
误区 7:4GHz 就是每秒执行 40 亿条指令
错误。
4GHz 描述时钟周期数量,不等于固定的每周期一条指令。现代 CPU 的流水线、IPC、缓存命中率、数据依赖、分支预测等都会影响真实吞吐量。
4. JVM 与物理 CPU 的概念误区
误区 8:JVM 的 PC Register 就是 CPU 的 RIP
错误。
JVM PC 是虚拟机规范中的每线程运行时概念,CPU 的 RIP 是 x86-64 的体系结构寄存器,两者属于不同抽象层。
八、完整执行链与总结
1. 从 Java 代码一直追到晶体管
现在回到最开始的:
int c = a + b;
它背后其实横跨了很多层。
从软件开发者的角度看,我们写的是:
a + b
从 JVM 的角度看,可能是:
iload
iload
iadd
istore
从 JIT 和 ISA 的角度看,它会变成目标平台机器指令。
再往下,机器指令进入 CPU,由真实的寄存器、执行单元、Cache 和控制结构完成计算。
而这些结构最底层,又是由晶圆上的海量晶体管组成的数字电路。
2. 总结
这篇文章真正需要记住的不是某一种 CPU 有多少根线,也不是半导体制造中的某一道工艺名称,而是几层抽象之间的关系:
物理材料
↓
晶体管
↓
数字逻辑
↓
处理器
↓
机器指令
↓
虚拟机字节码
↓
高级语言
高级语言把底层复杂性一层层藏起来,所以我们可以只写:
int c = a + b;
但黑盒并没有消失。
当我们继续研究 JVM、JIT、并发、内存屏障、Cache、系统调用甚至性能优化时,这个黑盒会重新一层层打开。
CPU 真正执行的是目标指令集的机器指令,而不是 Java 源代码;Java 之所以能够运行在 CPU 上,是因为 JVM 把统一的字节码语义继续解释或编译成本地机器码,而机器码最终又落在由晶体管构成的真实数字电路上。
评论区