案例介绍
本案例通过java代码实现jvm规范中指令集和解释器,完成后就可以开始执行1到100的加和计算。
Java虚拟机顾名思义,就是一台虚拟的机器,而字节码(bytecode)就是运行在这台虚拟机器上的机器码。我们已经知道,每一个类或者接口都会被Java编译器编译成一个class文件,类或接口的方法信息就放在class文件的method_info结构中。如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存在method_info结构的Code属性中。
环境准备
- jdk 1.8.0
- IntelliJ IDEA Community Edition 2018.3.1 x64
配置信息
- 调试配置
- 配置位置:Run/Debug Configurations -> program arguments
- 配置内容:-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
微信搜索「bugstack虫洞栈」公众号,关注后回复「用Java实现jvm源码」获取本文源码&更多原创专题案例!
bugstack虫洞栈
沉淀、分享、成长,让自己和他人都能有所收获
关注公众号,回复源码下载,获取整套案例
(转载本站文章请注明作者和出处 微信公众号:bugstack虫洞栈 | 作者:小傅哥)
Show Disqus Comments
Post Directory
扫码或搜索:bugstack虫洞栈
发送 290992
即可立即永久解锁本站全部文章