自己实现一个简易 JVM 是一项非常酷的挑战,它能让你真正理解 Java 字节码的执行机制、类加载流程、栈帧结构等底层原理。虽然完整 JVM 非常复杂,但我们可以从一个最小可运行版本开始,逐步构建出一个能解释 .class 文件的虚拟机。
简易 JVM 的核心模块
下面是构建一个简易 JVM 所需的核心组件:
最小运行示例:解释一个 HelloWorld.class
你可以参考这个项目:
HaidnorJVM(Java 编写的简易 JVM)
自己写 JVM 系列教程(CSDN)
知乎上的实现经验分享
这些项目都从 class 文件解析开始,逐步实现了字节码解释器,甚至支持运行 Java 程序中的 main() 方法。
实现步骤概览
第一步:读取并解析 .class 文件
按照 Java Class File Format 规范解析
解析常量池、字段、方法、属性等结构
第二步:构建运行时数据结构
模拟 JVM 的栈帧结构
每个方法调用创建一个栈帧,包含局部变量表和操作数栈
第三步:解释执行字节码
实现一个字节码解释器,支持常见指令:
加载指令:
iload,aload运算指令:
iadd,isub控制流:
goto,if_icmpge方法调用:
invokestatic,invokevirtual
第四步:支持基本输出
实现对
System.out.println的支持(通过模拟getstatic和invokevirtual)
示例代码片段(伪代码)
while (pc < bytecode.length) {
int opcode = bytecode[pc];
switch (opcode) {
case 0x10: // bipush
int value = bytecode[++pc];
operandStack.push(value);
pc++;
break;
case 0x60: // iadd
int b = operandStack.pop();
int a = operandStack.pop();
operandStack.push(a + b);
pc++;
break;
case 0xb1: // return
return;
// ...
}
}从零开始写一个最简易的 JVM,目标是能解释执行一个 Java 方法,比如 add(1, 2),并输出结果。我们先实现核心模块:字节码解释器 + 栈帧模拟器 + 方法调用机制。
项目结构(简化版)
mini-jvm/
├── Main.java // 启动入口
├── BytecodeInterpreter.java // 字节码解释器
├── Frame.java // 栈帧结构
├── MethodArea.java // 方法区(模拟类和方法)
└── Method.java // 方法定义1. Frame.java(栈帧结构)
import java.util.Stack;
public class Frame {
public int[] localVariables = new int[10]; // 局部变量表
public Stack<Integer> operandStack = new Stack<>(); // 操作数栈
}2. Method.java(方法定义)
public class Method {
public byte[] bytecode;
public int maxLocals;
public int maxStack;
public Method(byte[] bytecode, int maxLocals, int maxStack) {
this.bytecode = bytecode;
this.maxLocals = maxLocals;
this.maxStack = maxStack;
}
}3. MethodArea.java(模拟方法区)
import java.util.HashMap;
import java.util.Map;
public class MethodArea {
private static final Map<String, Method> methods = new HashMap<>();
public static void register(String name, Method method) {
methods.put(name, method);
}
public static Method find(String name) {
return methods.get(name);
}
}4. BytecodeInterpreter.java(字节码解释器)
public class BytecodeInterpreter {
public static void execute(Method method, int... args) {
Frame frame = new Frame();
for (int i = 0; i < args.length; i++) {
frame.localVariables[i] = args[i];
}
byte[] code = method.bytecode;
int pc = 0;
while (pc < code.length) {
int opcode = code[pc];
switch (opcode) {
case 0x1a: // iload_0
frame.operandStack.push(frame.localVariables[0]);
pc++;
break;
case 0x1b: // iload_1
frame.operandStack.push(frame.localVariables[1]);
pc++;
break;
case 0x60: // iadd
int b = frame.operandStack.pop();
int a = frame.operandStack.pop();
frame.operandStack.push(a + b);
pc++;
break;
case 0xac: // ireturn
int result = frame.operandStack.pop();
System.out.println("执行结果:" + result);
return;
default:
throw new RuntimeException("未知指令: " + opcode);
}
}
}
}5. Main.java(启动入口)
public class Main {
public static void main(String[] args) {
// 模拟 add(int a, int b) { return a + b; }
byte[] bytecode = {
0x1a, // iload_0
0x1b, // iload_1
0x60, // iadd
(byte) 0xac // ireturn
};
Method addMethod = new Method(bytecode, 2, 2);
MethodArea.register("add", addMethod);
Method method = MethodArea.find("add");
BytecodeInterpreter.execute(method, 3, 5); // 输出:执行结果:8
}
}运行结果
执行结果:8下一步可以扩展:
支持更多指令(如
istore,if_icmpge,goto)支持方法调用栈(多层 Frame)
支持类加载器和常量池
支持对象和字段访问