自己实现一个简易 JVM 是一项非常酷的挑战,它能让你真正理解 Java 字节码的执行机制、类加载流程、栈帧结构等底层原理。虽然完整 JVM 非常复杂,但我们可以从一个最小可运行版本开始,逐步构建出一个能解释 .class 文件的虚拟机。

简易 JVM 的核心模块

下面是构建一个简易 JVM 所需的核心组件:

模块

功能描述

类加载器(ClassLoader)

读取 .class

文件并解析为结构化数据

字节码解析器

解析 class 文件格式,包括常量池、方法表、字段表等

运行时数据区

模拟 JVM 的方法区、堆、栈等结构

栈帧管理器

每次方法调用创建栈帧,管理局部变量表和操作数栈

字节码解释器

逐条解释执行字节码指令(如 iload

, iadd

, invokevirtual

方法调用机制

支持静态方法、实例方法的调用与返回

基本类型支持

支持 int、float、boolean 等基本类型运算

输出机制

支持 System.out.println

等基础输出指令

最小运行示例:解释一个 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 的支持(通过模拟 getstaticinvokevirtual

示例代码片段(伪代码)

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)

  • 支持类加载器和常量池

  • 支持对象和字段访问