案例介绍
本案例通过java代码实现jvm规范中指令集和解释器,完成后就可以开始执行1到100的加和计算。
Java虚拟机顾名思义,就是一台虚拟的机器,而字节码(bytecode)就是运行在这台虚拟机器上的机器码。我们已经知道,每一个类或者接口都会被Java编译器编译成一个class文件,类或接口的方法信息就放在class文件的method_info结构中。如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存在method_info结构的Code属性中。
环境准备
1、jdk 1.8.0
2、IntelliJ IDEA Community Edition 2018.3.1 x64
配置信息
1、调试配置
2.1、配置位置:Run/Debug Configurations -> program arguments
2.2、配置内容:-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld
代码示例
itstack-demo-jvm-05 ├── pom.xml └── src └── main │ └── java │ └── org.itstack.demo.jvm │ ├── classfile │ │ ├── attributes {BootstrapMethods/Code/ConstantValue...} │ │ ├── constantpool {CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...} │ │ ├── ClassFile.java │ │ ├── ClassReader.java │ │ └── MemberInfo.java │ ├── classpath │ │ ├── impl │ │ │ ├── CompositeEntry.java │ │ │ ├── DirEntry.java │ │ │ ├── WildcardEntry.java │ │ │ └── ZipEntry.java │ │ ├── Classpath.java │ │ └── Entry.java │ ├── classpath │ │ ├── base │ │ ├── comparisons │ │ ├── constants │ │ ├── control │ │ ├── conversions │ │ ├── extended │ │ ├── loads │ │ ├── math │ │ │ ├── add │ │ │ ├── and │ │ │ ├── div │ │ │ ├── iinc │ │ │ ├── mul │ │ │ ├── neg │ │ │ ├── or │ │ │ ├── rem │ │ │ ├── sh │ │ │ ├── sub │ │ │ └── xor │ │ ├── stack │ │ ├── store │ │ └── Factory │ ├── rtda │ │ ├── Frame.java │ │ ├── JvmStack.java │ │ ├── LocalVars.java │ │ ├── OperandStack.java │ │ ├── Slot.java │ │ └── Thread.java │ ├── Cmd.java │ ├── Interpret.java │ └── Main.java └── test └── java └── org.itstack.demo.test └── HelloWorld.java
Factory.java
public class Factory { public static Instruction newInstruction(byte opcode) { switch (opcode) { case 0x00: return new NOP(); case 0x01: return new ACONST_NULL(); case 0x02: return new ICONST_M1(); case 0x03: return new ICONST_0(); case 0x04: return new ICONST_1(); case 0x05: return new ICONST_2(); case 0x06: return new ICONST_3(); case 0x07: return new ICONST_4(); case 0x08: return new ICONST_5(); case 0x09: return new LCONST_0(); case 0x0a: return new LCONST_1(); case 0x0b: return new FCONST_0(); case 0x0c: return new FCONST_1(); case 0x0d: return new FCONST_2(); case 0x0e: return new DCONST_0(); case 0x0f: return new DCONST_1(); case 0x10: return new BIPUSH(); case 0x11: return new SIPUSH(); // case 0x12: // return &LDC{} // case 0x13: // return &LDC_W{} // case 0x14: // return &LDC2_W{} case 0x15: return new ILOAD(); case 0x16: return new LLOAD(); case 0x17: return new FLOAD(); case 0x18: return new DLOAD(); case 0x19: return new ALOAD(); case 0x1a: return new ILOAD_0(); case 0x1b: return new ILOAD_1(); case 0x1c: return new ILOAD_2(); case 0x1d: return new ILOAD_3(); case 0x1e: return new LLOAD_0(); case 0x1f: return new LLOAD_1(); case 0x20: return new LLOAD_2(); case 0x21: return new LLOAD_3(); case 0x22: return new FLOAD_0(); case 0x23: return new FLOAD_1(); case 0x24: return new FLOAD_2(); case 0x25: return new FLOAD_3(); case 0x26: return new DLOAD_0(); case 0x27: return new DLOAD_1(); case 0x28: return new DLOAD_2(); case 0x29: return new DLOAD_3(); case 0x2a: return new ALOAD_0(); case 0x2b: return new ALOAD_1(); case 0x2c: return new ALOAD_2(); case 0x2d: return new ALOAD_3(); // case 0x2e: // return iaload // case 0x2f: // return laload // case 0x30: // return faload // case 0x31: // return daload // case 0x32: // return aaload // case 0x33: // return baload // case 0x34: // return caload // case 0x35: // return saload case 0x36: return new ISTORE(); case 0x37: return new LSTORE(); case 0x38: return new FSTORE(); case 0x39: return new DSTORE(); case 0x3a: return new ASTORE(); case 0x3b: return new ISTORE_0(); case 0x3c: return new ISTORE_1(); case 0x3d: return new ISTORE_2(); case 0x3e: return new ISTORE_3(); case 0x3f: return new LSTORE_0(); case 0x40: return new LSTORE_1(); case 0x41: return new LSTORE_2(); case 0x42: return new LSTORE_3(); case 0x43: return new FSTORE_0(); case 0x44: return new FSTORE_1(); case 0x45: return new FSTORE_2(); case 0x46: return new FSTORE_3(); case 0x47: return new DSTORE_0(); case 0x48: return new DSTORE_1(); case 0x49: return new DSTORE_2(); case 0x4a: return new DSTORE_3(); case 0x4b: return new ASTORE_0(); case 0x4c: return new ASTORE_1(); case 0x4d: return new ASTORE_2(); case 0x4e: return new ASTORE_3(); // case 0x4f: // return iastore // case 0x50: // return lastore // case 0x51: // return fastore // case 0x52: // return dastore // case 0x53: // return aastore // case 0x54: // return bastore // case 0x55: // return castore // case 0x56: // return sastore case 0x57: return new POP(); case 0x58: return new POP2(); case 0x59: return new DUP(); case 0x5a: return new DUP_X1(); case 0x5b: return new DUP_X2(); case 0x5c: return new DUP2(); case 0x5d: return new DUP2_X1(); case 0x5e: return new DUP2_X2(); case 0x5f: return new SWAP(); case 0x60: return new IADD(); case 0x61: return new LADD(); case 0x62: return new FADD(); case 0x63: return new DADD(); case 0x64: return new ISUB(); case 0x65: return new LSUB(); case 0x66: return new FSUB(); case 0x67: return new DSUB(); case 0x68: return new IMUL(); case 0x69: return new LMUL(); case 0x6a: return new FMUL(); case 0x6b: return new DMUL(); case 0x6c: return new IDIV(); case 0x6d: return new LDIV(); case 0x6e: return new FDIV(); case 0x6f: return new DDIV(); case 0x70: return new IREM(); case 0x71: return new LREM(); case 0x72: return new FREM(); case 0x73: return new DREM(); case 0x74: return new INEG(); case 0x75: return new LNEG(); case 0x76: return new FNEG(); case 0x77: return new DNEG(); case 0x78: return new ISHL(); case 0x79: return new LSHL(); case 0x7a: return new ISHR(); case 0x7b: return new LSHR(); case 0x7c: return new IUSHR(); case 0x7d: return new LUSHR(); case 0x7e: return new IAND(); case 0x7f: return new LAND(); case (byte) 0x80: return new IOR(); case (byte) 0x81: return new LOR(); case (byte) 0x82: return new IXOR(); case (byte) 0x83: return new LXOR(); case (byte) 0x84: return new IINC(); case (byte) 0x85: return new I2L(); case (byte) 0x86: return new I2F(); case (byte) 0x87: return new I2D(); case (byte) 0x88: return new L2I(); case (byte) 0x89: return new L2F(); case (byte) 0x8a: return new L2D(); case (byte) 0x8b: return new F2I(); case (byte) 0x8c: return new F2L(); case (byte) 0x8d: return new F2D(); case (byte) 0x8e: return new D2I(); case (byte) 0x8f: return new D2L(); case (byte) 0x90: return new D2F(); case (byte) 0x91: return new I2B(); case (byte) 0x92: return new I2C(); case (byte) 0x93: return new I2S(); case (byte) 0x94: return new LCMP(); case (byte) 0x95: return new FCMPL(); case (byte) 0x96: return new FCMPG(); case (byte) 0x97: return new DCMPL(); case (byte) 0x98: return new DCMPG(); case (byte) 0x99: return new IFEQ(); case (byte) 0x9a: return new IFNE(); case (byte) 0x9b: return new IFLT(); case (byte) 0x9c: return new IFGE(); case (byte) 0x9d: return new IFGT(); case (byte) 0x9e: return new IFLE(); case (byte) 0x9f: return new IF_ICMPEQ(); case (byte) 0xa0: return new IF_ICMPNE(); case (byte) 0xa1: return new IF_ICMPLT(); case (byte) 0xa2: return new IF_ICMPGE(); case (byte) 0xa3: return new IF_ICMPGT(); case (byte) 0xa4: return new IF_ICMPLE(); case (byte) 0xa5: return new IF_ACMPEQ(); case (byte) 0xa6: return new IF_ACMPNE(); case (byte) 0xa7: return new GOTO(); // case 0xa8: // return &JSR{} // case 0xa9: // return &RET{} case (byte) 0xaa: return new TABLE_SWITCH(); case (byte) 0xab: return new LOOKUP_SWITCH(); // case 0xac: // return ireturn // case 0xad: // return lreturn // case 0xae: // return freturn // case 0xaf: // return dreturn // case 0xb0: // return areturn // case 0xb1: // return _return // case 0xb2: // return &GET_STATIC{} // case 0xb3: // return &PUT_STATIC{} // case 0xb4: // return &GET_FIELD{} // case 0xb5: // return &PUT_FIELD{} // case 0xb6: // return &INVOKE_VIRTUAL{} // case 0xb7: // return &INVOKE_SPECIAL{} // case 0xb8: // return &INVOKE_STATIC{} // case 0xb9: // return &INVOKE_INTERFACE{} // case 0xba: // return &INVOKE_DYNAMIC{} // case 0xbb: // return &NEW{} // case 0xbc: // return &NEW_ARRAY{} // case 0xbd: // return &ANEW_ARRAY{} // case 0xbe: // return arraylength // case 0xbf: // return athrow // case 0xc0: // return &CHECK_CAST{} // case 0xc1: // return &INSTANCE_OF{} // case 0xc2: // return monitorenter // case 0xc3: // return monitorexit case (byte) 0xc4: return new WIDE(); // case 0xc5: // return &MULTI_ANEW_ARRAY{} case (byte) 0xc6: return new IFNULL(); case (byte) 0xc7: return new IFNONNULL(); case (byte) 0xc8: return new GOTO_W(); // case 0xc9: // return &JSR_W{} // case 0xca: breakpoint // case 0xfe: impdep1 // case 0xff: impdep2 default: return null; } } }
Interpret.java
//指令集解释器 class Interpret { Interpret(MemberInfo m) { CodeAttribute codeAttr = m.codeAttribute(); int maxLocals = codeAttr.maxLocals(); int maxStack = codeAttr.maxStack(); byte[] byteCode = codeAttr.data(); Thread thread = new Thread(); Frame frame = thread.newFrame(maxLocals, maxStack); thread.pushFrame(frame); loop(thread, byteCode); } private void loop(Thread thread, byte[] byteCode) { Frame frame = thread.popFrame(); BytecodeReader reader = new BytecodeReader(); while (true) { //循环 int pc = frame.nextPC(); thread.setPC(pc); //decode reader.reset(byteCode, pc); byte opcode = reader.readByte(); Instruction inst = Factory.newInstruction(opcode); if (null == inst) { System.out.println("寄存器(指令)尚未实现 " + byteToHexString(new byte[]{opcode})); break; } inst.fetchOperands(reader); frame.setNextPC(reader.pc()); System.out.println("寄存器(指令):" + byteToHexString(new byte[]{opcode}) + " -> " + inst.getClass().getSimpleName() + " => 局部变量表:" + JSON.toJSONString(frame.operandStack().getSlots()) + " 操作数栈:" + JSON.toJSONString(frame.operandStack().getSlots())); //exec inst.execute(frame); } } private static String byteToHexString(byte[] codes) { StringBuilder sb = new StringBuilder(); sb.append("0x"); for (byte b : codes) { int value = b & 0xFF; String strHex = Integer.toHexString(value); if (strHex.length() < 2) { strHex = "0" + strHex; } sb.append(strHex); } return sb.toString(); } }
HelloWorld.java
package org.itstack.demo.test; public class HelloWorld { public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } System.out.println(sum); } }
测试结果
{此时还不能输出结果,但是在操作数据栈中已经可以看到结果:5050}
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\lib\idea_rt.jar=61887:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\classes;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\org\projectlombok\lombok\1.18.0\lombok-1.18.0.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\alibaba\fastjson\1.2.40\fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld args:null 寄存器(指令):0x03 -> ICONST_0 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}] 寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}] 寄存器(指令):0x04 -> ICONST_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}] 寄存器(指令):0x3d -> ISTORE_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}] 寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}] 寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}] 寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}] 寄存器(指令):0x1b -> ILOAD_1 => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}] 寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":0},{"num":100}] 操作数栈:[{"num":0},{"num":100}] ... ... 寄存器(指令):0x60 -> IADD => 局部变量表:[{"num":4950},{"num":100}] 操作数栈:[{"num":4950},{"num":100}] 寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}] 寄存器(指令):0x84 -> IINC => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}] 寄存器(指令):0xa7 -> GOTO => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}] 寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}] 寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}] 寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}] 寄存器(指令)尚未实现 0xb2 Process finished with exit code 0