Skip to content

字节码与虚拟机 ​

字节码是中间表示形式,虚拟机负责执行这些字节码指令。这两者合起来决定了 Lua 代码的执行方式。

1. 字节码(Bytecode) ​

字节码是Lua解释器生成的低级中间表示形式,比源代码更接近机器语言,仍然需要通过虚拟机来执行。它是一系列指令,每条指令定义了虚拟机执行的一个操作。

字节码有三个特性值得说明。第一,与平台弱相关:字节码由 Lua 官方统一定义,同一版本的 Lua 在不同平台上生成的字节码格式相同,但官方明确说明预编译的二进制块(luac -o 生成的文件)并不保证跨平台可移植,字节序、整数和浮点数宽度不同的机器之间可能无法互相加载,使用时应在目标平台上重新编译。第二,结构更简单、操作更明确,比源代码更容易被虚拟机解析和执行。第三,编译阶段会做一些局部优化,例如常量折叠(10 + 5 在编译期直接算成 15)。

字节码由Lua解释器在编译阶段生成:编译器将Lua源代码转换为字节码,供虚拟机执行。

用 luac -l -l 可以查看真实生成的字节码。对于

lua
-- 源代码
local a = 10 + 5

Lua 5.4 的输出为(节选):

plaintext
main <t.lua:0,0> (3 instructions)
	1	[1]	VARARGPREP	0
	2	[1]	LOADI    	0 15     ; 把整数 15 装入寄存器 0(即局部变量 a)
	3	[1]	RETURN   	1 1 1	; 0 out

注意两点:10 + 5 已在编译期被常量折叠成 15,所以只需要一条 LOADI 指令;寄存器编号从 0 开始,局部变量 a 就存放在寄存器 0 中。

2. 虚拟机(Virtual Machine) ​

虚拟机是执行字节码的引擎:读取字节码指令、解析,并在运行时执行相应的操作。它提供了一个抽象的执行环境,Lua程序可以在各种平台上运行而无需修改代码。

虚拟机使用寄存器模型处理数据,寄存器存放中间结果和操作数,而不是传统的堆栈模型。指令集是字节码的一部分,定义了支持的操作,例如加载数据、执行算术运算、调用函数等。内存管理和垃圾回收也是虚拟机的职责。

执行流程分四步:加载字节码(从文件或内存),逐条解析指令,按指令类型执行操作(算术运算、函数调用等),最后更新寄存器的值、管理函数调用栈和局部变量、执行内存分配和回收。

示例:

plaintext
-- 字节码示例(local a = 10 + 5,Lua 5.4 实际输出)
1	[1]	VARARGPREP	0          ; main chunk 的准备动作
2	[1]	LOADI    	0 15       ; 寄存器 0 ← 整数 15
3	[1]	RETURN   	1 1 1	; 0 out

-- 虚拟机执行过程
1. 加载字节码(luac -o 生成的二进制块,或由 load/require 编译的字符串)
2. 执行 VARARGPREP,完成主函数的可变参数准备
3. 执行 LOADI,把常量 15 写入寄存器 0(局部变量 a)
4. 执行 RETURN,返回 1 个返回值并结束

总结 ​

字节码定义程序的操作,虚拟机解析并执行这些指令,同时负责内存管理和垃圾回收。了解这条链路,排查性能问题和调试字节码时就有据可依。

Hello Lua