diff --git a/README.md b/README.md index 7fc8dac..562f3af 100644 --- a/README.md +++ b/README.md @@ -2,31 +2,113 @@ 这是一个Java实现的JAVA虚拟机,它会非常简单,实际上简单的只够运行HelloWorld。虽然简单,但尽量符合 JVM 标准,目前主要参考依据是[《Java虚拟机规范 (Java SE 7 中文版)》](http://www.iteye.com/topic/1117824)。 -关于此项目的说明,[详见此文](http://www.jianshu.com/p/4d81465c2fb8)。 -## 运行 +非原创,原项目来自 https://github.com/caoym/jjvm -先写一个HelloWorld,代码如下: ++ 运行方式有改动 (原项目运行方式在我的环境不work) ++ pc处理机制有修改 + + 原实现无法支持条件跳转 -```java -package org.caoym; ++ opcode 新增指令 -public class HelloWorld { - public static void main(String[] args){ - System.out.println("Hello World"); - } -} + 增加if_cmple,goto + 增加iload_n + + +## 参考项目 ++ java版jvm实现( https://github.com/zachaxy/JVM ) ++ go版jvm实现( https://github.com/zxh0/jvmgo-book ) + + +# 用法 + +## 环境 +SUN jdk1.8 (OpenJdk1.8 运行缺少一些jar,用SUN JDK重装后解决) + +```shell +$java -version +java version "1.8.0_211" +Java(TM) SE Runtime Environment (build 1.8.0_211-b12) +Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode) ``` -可以通过以下命令运行: +### 设置java环境 +将以下内容加入 /etc/profile ,保存后执行 source /etc/profile 使之生效 ```shell -$ java /home/myclasspath org.caoym.jjvm.JJvm org.caoym.HelloWorld -Hello World +JAVA_HOME=/usr/java/jdk1.8.0_211-amd64 +JRE_HOME=$JAVA_HOME/jre +PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin +CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib +export JAVA_HOME JRE_HOME PATH CLASSPATH ``` -## 可运行的示例 +## 编译和运行 +可以通过以下编译命令运行: +### Sample1 +基本HelloWorld -[用于测试 JJvm 的示例代码可在此处下载](https://github.com/caoym/jjvm-samples) +```shell +#编译 sample1 +$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true +$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked +$javac org/caoym/samples/sample1/HelloWorld.java + +#运行 +$java org.caoym.jjvm.JJvm . org.caoym.samples.sample1.HelloWorld +``` + +### Sample2 +带有Interface、父子类继承的Helloworld + +```shell +#编译 sample2 +$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true +$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked +$javac org/caoym/samples/sample2/Main.java + +#运行(不加运行参数如inputVar1,会报错) +$java org.caoym.jjvm.JJvm . org.caoym.samples.sample2.Main inputVar1 +``` +### Sample3 +带有条件跳转的Helloworld +```shell +#编译 sample3 +$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true +$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked +$javac org/caoym/samples/sample3/HelloWorld.java + +#运行 +$java org.caoym.jjvm.JJvm . org.caoym.samples.sample3.HelloWorld +``` + +### Sample4 +线程 + +```shell +#编译 sample4 +$javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true +$javac org/caoym/jjvm/JJvm.java -Xlint:unchecked +$javac org/caoym/samples/sample4/TestThread.java + +#运行 +$java org.caoym.jjvm.JJvm . org.caoym.samples.sample4.TestThread +``` + +## FAQ +### error: package jdk.internal.org.objectweb.asm does not exist +```shell +默认 javac 不读取 rt.jar 中 classes内容,只读取符号表 + 符号表一般只包括标准API和部分其他API,如 com.sun., com.oracle. and sun.* + +编译选项加上 -XDignore.symbol.file=true + javac org/caoym/jjvm/JJvm.java -XDignore.symbol.file=true +``` + +### 未能自动清理旧class文件,重新编译前手动清理一下,执行 +```shell +$find . -name *.class -type f -print -exec rm -rf {} \; +``` diff --git a/src/main/java/org/caoym/jjvm/VirtualMachine.java b/src/main/java/org/caoym/jjvm/VirtualMachine.java index 1a2133a..e3f3f6c 100644 --- a/src/main/java/org/caoym/jjvm/VirtualMachine.java +++ b/src/main/java/org/caoym/jjvm/VirtualMachine.java @@ -41,7 +41,7 @@ public void run(String[] args) throws Exception { "main", "([Ljava/lang/String;)V"); //执行入口方法 - method.call(env, null, new Object[]{args}); + method.call(env, null, new Object[]{args});//TODO:多个class文件,是如何组织?何时加载的? } public JvmClass getClass(String className) throws ClassNotFoundException { diff --git a/src/main/java/org/caoym/jjvm/opcode/BytecodeInterpreter.java b/src/main/java/org/caoym/jjvm/opcode/BytecodeInterpreter.java index 6a64a18..13b4ac9 100644 --- a/src/main/java/org/caoym/jjvm/opcode/BytecodeInterpreter.java +++ b/src/main/java/org/caoym/jjvm/opcode/BytecodeInterpreter.java @@ -34,8 +34,9 @@ public static void run(Env env) throws Exception { } continue; } - OpcodeInvoker[] codes = frame.getOpcodes(); - int pc = frame.increasePC(); + OpcodeInvoker[] ops = frame.getOps(); + int pc = frame.getPC(); + StringBuilder sb = new StringBuilder(); sb.append("> "); sb.append(frame.getCurrentClass().getName()); @@ -44,12 +45,30 @@ public static void run(Env env) throws Exception { sb.append("@"); sb.append(pc); sb.append(":"); - sb.append(codes[pc]); + sb.append(ops[pc]); + sb.append("["); + sb.append(ops[pc].getNoOfOperands()); + sb.append("]"); System.out.println(sb); - codes[pc].invoke(env, frame); + + //execute opcode + int ret = ops[pc].invoke(env, frame); + + //move forward + if(ret == 0) { + frame.increasePC(1+ops[pc].getNoOfOperands()); + }else { + frame.increasePC(pc + ret); + } } } + /** + * 返回内容以 opcode 为主 + * + * @param codes + * @return + */ public static OpcodeInvoker[] parseCodes(byte[] codes){ ArrayList opcodes = new ArrayList<>(); for(int i=0; i ops = new ArrayList<>(); + for(int i=0; i args = frame.getOperandStack().multiPop(method.getParameterCount()); + Collections.reverse(args); + Object[] argsArr = args.toArray(); + JvmObject thiz = (JvmObject) argsArr[0]; + + //根据类名确定是调用父类还是子类 + while (!thiz.getClazz().getName().equals(clazz.getName())){ + thiz = thiz.getSuper(); + } + method.call(env, thiz, Arrays.copyOfRange(argsArr,1, argsArr.length)); + return 0; } }, /** @@ -114,19 +153,85 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ LDC(Constants.LDC){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { int arg = operands[0]; ConstantPool.CPInfo info = frame.getConstantPool().get(arg); frame.getOperandStack().push(asObject(info), 1); + return 0; } }, + /** + * 将int, float或String型常量值从常量池中推送至栈顶(宽索引) + */ + LDC_W(Constants.LDC_W){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + int arg = (operands[0]<<8)|operands[1]; + ConstantPool.CPInfo info = frame.getConstantPool().get(arg); + frame.getOperandStack().push(asObject(info), 1); + return 0; + } + }, + /** + * 将long或double型常量值从常量池中推送至栈顶(宽索引) + */ + LDC2_W(Constants.LDC2_W){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + int arg = (operands[0]<<8)|operands[1]; + ConstantPool.CPInfo info = frame.getConstantPool().get(arg); + frame.getOperandStack().push(asObject(info), 1); + return 0; + } + }, + /** + * 将栈顶引用型数值存入第0个本地变量 + */ + ASTORE_0(Constants.ASTORE_0){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getLocalVariables().set(0, frame.getOperandStack().pop(), 1); + return 0; + } + }, + /** + * 将栈顶引用型数值存入第1个本地变量 + */ + ASTORE_1(Constants.ASTORE_1){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getLocalVariables().set(1, frame.getOperandStack().pop(), 1); + return 0; + } + }, + /** + * 将栈顶引用型数值存入第2个本地变量 + */ + ASTORE_2(Constants.ASTORE_2){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getLocalVariables().set(2, frame.getOperandStack().pop(), 1); + return 0; + } + }, + /** + * 将栈顶引用型数值存入第3个本地变量 + */ + ASTORE_3(Constants.ASTORE_3){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getLocalVariables().set(3, frame.getOperandStack().pop(), 1); + return 0; + } + }, /** * 调用实例方法 */ INVOKEVIRTUAL(Constants.INVOKEVIRTUAL){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { INVOKESPECIAL.invoke(env, frame, operands); + return 0; } }, /** @@ -134,8 +239,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ DCONST_0(Constants.DCONST_0){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(0.0,2); + return 0; } }, /** @@ -143,9 +249,10 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ DSTORE_1(Constants.DSTORE_1){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Object var = frame.getOperandStack().pop(); frame.getLocalVariables().set(1, var, 2); + return 0; } }, /** @@ -153,17 +260,19 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ DLOAD_1(Constants.DLOAD_1){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Object var = frame.getLocalVariables().get(1); frame.getOperandStack().push(var, 2); + return 0; } }, //dconst_1: 将 double 型 1 推送至栈顶 DCONST_1(Constants.DCONST_1){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(1.0, 2); + return 0; } }, /** @@ -171,10 +280,11 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ DADD(Constants.DADD){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Double var1 = (Double) frame.getOperandStack().pop(); Double var2 = (Double) frame.getOperandStack().pop(); frame.getOperandStack().push(var1 + var2, 2); + return 0; } }, /** @@ -182,52 +292,69 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ICONST_0(Constants.ICONST_0){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(0,1); + return 0; } }, //iconst_1: 将 int 型 1 推送至栈顶 ICONST_1(Constants.ICONST_1){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(1, 1); + return 0; } }, //iconst_2: 将 int 型 2 推送至栈顶 ICONST_2(Constants.ICONST_2){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(2, 1); + return 0; } }, //iconst_3: 将 int 型 3 推送至栈顶 ICONST_3(Constants.ICONST_3){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(3, 1); + return 0; } }, //iconst_4: 将 int 型 4 推送至栈顶 ICONST_4(Constants.ICONST_4){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(4, 1); + return 0; } }, //iconst_5: 将 int 型 5 推送至栈顶 ICONST_5(Constants.ICONST_5){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(5, 1); + return 0; } }, + /** + * 将栈顶 int 型数值存入指定本地变量 + */ + ISTORE(Constants.ISTORE){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getLocalVariables().set(0, frame.getOperandStack().pop(), 1); + return 0; + } + }, /** * 将栈顶 int 型数值存入第0个局部变量 */ ISTORE_0(Constants.ISTORE_0){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getLocalVariables().set(0, frame.getOperandStack().pop(), 1); + return 0; } }, /** @@ -235,8 +362,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ISTORE_1(Constants.ISTORE_1){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getLocalVariables().set(1, frame.getOperandStack().pop(), 1); + return 0; } }, /** @@ -244,8 +372,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ISTORE_2(Constants.ISTORE_2){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getLocalVariables().set(2, frame.getOperandStack().pop(), 1); + return 0; } }, /** @@ -253,8 +382,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ISTORE_3(Constants.ISTORE_3){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getLocalVariables().set(3, frame.getOperandStack().pop(), 1); + return 0; } }, /** @@ -262,8 +392,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ILOAD_0(Constants.ILOAD_0){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(frame.getLocalVariables().get(0), 1); + return 0; } }, /** @@ -271,8 +402,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ILOAD_1(Constants.ILOAD_1){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(frame.getLocalVariables().get(1), 1); + return 0; } }, /** @@ -280,8 +412,9 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ILOAD_2(Constants.ILOAD_2){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(frame.getLocalVariables().get(2), 1); + return 0; } }, /** @@ -289,18 +422,60 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ ILOAD_3(Constants.ILOAD_3){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(frame.getLocalVariables().get(3), 1); + return 0; } }, + /** + * 将第0个 long 型局部变量进栈 + */ + LLOAD_0(Constants.LLOAD_0){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getOperandStack().push(frame.getLocalVariables().get(0), 1); + return 0; + } + }, + /** + * 第1个long型局部变量进栈 + */ + LLOAD_1(Constants.LLOAD_1){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getOperandStack().push(frame.getLocalVariables().get(1), 1); + return 0; + } + }, + /** + * 第2个long型局部变量进栈 + */ + LLOAD_2(Constants.LLOAD_2){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getOperandStack().push(frame.getLocalVariables().get(2), 1); + return 0; + } + }, + /** + * 第3个long型局部变量进栈 + */ + LLOAD_3(Constants.LLOAD_3){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + frame.getOperandStack().push(frame.getLocalVariables().get(3), 1); + return 0; + } + }, /** * 将指定 int 型变量增加指定值。 */ IINC(Constants.IINC){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Integer var = (Integer) frame.getLocalVariables().get(operands[0]); frame.getLocalVariables().set(operands[0], var + operands[1], 1); + return 0; } }, /** @@ -308,9 +483,10 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ I2D(Constants.I2D){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Integer var = (Integer) frame.getOperandStack().pop(); frame.getOperandStack().push((double) var, 2); + return 0; } }, /** @@ -318,10 +494,13 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ AALOAD(Constants.AALOAD){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { int index = (int) frame.getOperandStack().pop(); Object[] arrayRef = (Object[]) frame.getOperandStack().pop(); - frame.getOperandStack().push(arrayRef[index]); + if( null != arrayRef && arrayRef.length > 0){ + frame.getOperandStack().push(arrayRef[index]); + } + return 0; } }, /** @@ -329,12 +508,24 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ NEW(Constants.NEW){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { int index = (operands[0] << 8)| operands[1]; ConstantPool.CONSTANT_Class_info info = (ConstantPool.CONSTANT_Class_info)frame.getConstantPool().get(index); JvmClass clazz = env.getVm().getClass(info.getName()); frame.getOperandStack().push(clazz.newInstance(env)); + return 0; + } + }, + /** + * array长度 + */ + ARRAYLENGTH(Constants.ARRAYLENGTH){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + Object[] arrayRef = (Object[]) frame.getOperandStack().pop(); + frame.getOperandStack().push(arrayRef.length); + return 0; } }, /** @@ -342,16 +533,87 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ DUP(Constants.DUP){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { frame.getOperandStack().push(frame.getOperandStack().pick(), frame.getOperandStack().getEndSize()); + return 0; } + }, + /** + * 当栈顶int型数值小于等于0时跳转 + */ + IFLE(Constants.IFLE){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + Integer var = (Integer) frame.getOperandStack().pop(); + if(var <= 0) { + int offset = (operands[0]<<8)|operands[1]; + return offset; + } + return 0; + } + }, + /** + * 比较运算符:if_icmple + * if value1 is less than or equal to value2, + * branch to instruction at branchoffset + * (signed short constructed from unsigned bytes branchbyte1 << 8 + branchbyte2) + */ + IF_ICMPLE(Constants.IF_ICMPLE){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + Integer var2 = (Integer) frame.getOperandStack().pop(); + Integer var1 = (Integer) frame.getOperandStack().pop(); + if(var1 <= var2) { + int offset = (operands[0]<<8)|operands[1]; + return offset; + } + return 0; + } + }, + /** + * 跳转运算符:goto + */ + GOTO(Constants.GOTO){ + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + int offset = (operands[0]<<8)|operands[1]; + return offset; + } + }, + /** + * 如果值为空,则跳转 + */ + IFNULL(Constants.IFNULL) { + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + Integer var = (Integer) frame.getOperandStack().pop(); + if(null == var) { + int offset = (operands[0]<<8)|operands[1]; + return offset; + } + return 0; + } + }, + /** + * 如果值非空,则跳转 + */ + IFNONNULL(Constants.IFNONNULL) { + @Override + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + Integer var = (Integer) frame.getOperandStack().pop(); + if(null != var) { + int offset = (operands[0]<<8)|operands[1]; + return offset; + } + return 0; + } }, /** * 为指定的类的静态域赋值。 */ PUTSTATIC(Constants.PUTSTATIC){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Object var = frame.getOperandStack().pop(); int index = (operands[0] << 8)| operands[1]; ConstantPool.CONSTANT_Fieldref_info info @@ -360,6 +622,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception JvmClass clazz = env.getVm().getClass(info.getClassName()); JvmField field = clazz.getField(info.getNameAndTypeInfo().getName()); field.set(env, null, var); + return 0; } }, /** @@ -367,7 +630,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ PUTFIELD(Constants.PUTFIELD){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Object value = frame.getOperandStack().pop(); Object objectref = frame.getOperandStack().pop(); @@ -378,6 +641,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception JvmField field = clazz.getField(info.getNameAndTypeInfo().getName()); assert field != null; field.set(env,objectref, value); + return 0; } }, /** @@ -385,7 +649,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ GETFIELD(Constants.GETFIELD){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { Object objectref = frame.getOperandStack().pop(); int index = (operands[0] << 8)| operands[1]; @@ -394,6 +658,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception JvmClass clazz = env.getVm().getClass(info.getClassName()); JvmField field = clazz.getField(info.getNameAndTypeInfo().getName()); frame.getOperandStack().push(field.get(env, objectref)); + return 0; } }, /** @@ -401,7 +666,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception */ INVOKEINTERFACE(Constants.INVOKEINTERFACE){ @Override - public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception { + public int invoke(Env env, StackFrame frame, byte[] operands) throws Exception { // 获取接口和方法信息 int arg = (operands[0]<<8)|operands[1]; @@ -422,7 +687,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception JvmObject thiz = (JvmObject)argsArr[0]; JvmMethod method = null; - //递归搜索接口方法 + //依次向上搜索接口方法 while(thiz != null){ if(thiz.getClazz().hasMethod(name, type)){ method = thiz.getClazz().getMethod(name, type); @@ -436,6 +701,7 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception } // 执行接口方法 method.call(env, thiz, Arrays.copyOfRange(argsArr,1, argsArr.length)); + return 0; } } ; @@ -448,8 +714,12 @@ public void invoke(Env env, StackFrame frame, byte[] operands) throws Exception this.code = code; } - public abstract void invoke(Env env, StackFrame frame, byte[] operands) throws Exception; + public abstract int invoke(Env env, StackFrame frame, byte[] operands) throws Exception; + public int getNoOfOperands() { + return 0; + } + public short getCode() { return code; } @@ -457,7 +727,7 @@ public short getCode() { public static OpcodeRout valueOf(short code){ OpcodeRout op = codeMapping.get(code); if(op == null){ - throw new InternalError("The opcode "+Constants.OPCODE_NAMES[code]+" Not Impl"); + throw new InternalError("The opcode ["+Constants.OPCODE_NAMES[code]+"] Not Impl"); } return op; } diff --git a/src/main/java/org/caoym/jjvm/runtime/JvmStack.java b/src/main/java/org/caoym/jjvm/runtime/JvmStack.java index 85d475f..91b294b 100644 --- a/src/main/java/org/caoym/jjvm/runtime/JvmStack.java +++ b/src/main/java/org/caoym/jjvm/runtime/JvmStack.java @@ -15,16 +15,17 @@ public class JvmStack { private boolean running = false; public StackFrame newFrame(JvmClass clazz, JvmMethod method) { - StackFrame frame = new StackFrame(clazz, method, null, null, 0, 0); + StackFrame frame = new StackFrame(clazz, method, null, null, null, 0, 0); frames.push(frame, 1); return frame; } public StackFrame newFrame(JvmClass clazz, JvmMethod method, ConstantPool constantPool, OpcodeInvoker[] opcodes, + OpcodeInvoker[] ops, int variables, int stackSize) { - StackFrame frame = new StackFrame(clazz, method, constantPool, opcodes, variables, stackSize); + StackFrame frame = new StackFrame(clazz, method, constantPool, opcodes, ops, variables, stackSize); frames.push(frame, 1); return frame; } diff --git a/src/main/java/org/caoym/jjvm/runtime/Slots.java b/src/main/java/org/caoym/jjvm/runtime/Slots.java index c84cdf6..80fe5c8 100644 --- a/src/main/java/org/caoym/jjvm/runtime/Slots.java +++ b/src/main/java/org/caoym/jjvm/runtime/Slots.java @@ -15,6 +15,7 @@ public Slots(int size){ public void set(int pos, T entity, int size) throws IllegalArgumentException{ if(pos <0 || pos+size > buffer.length){ + //不支持动态扩容量 throw new IllegalArgumentException("invalid entity size "+size); } buffer[pos] = entity; diff --git a/src/main/java/org/caoym/jjvm/runtime/SlotsStack.java b/src/main/java/org/caoym/jjvm/runtime/SlotsStack.java index 1eb078d..913fd45 100644 --- a/src/main/java/org/caoym/jjvm/runtime/SlotsStack.java +++ b/src/main/java/org/caoym/jjvm/runtime/SlotsStack.java @@ -22,6 +22,7 @@ public void push(T entity) throws IllegalArgumentException{ public void push(T entity, int size) throws IllegalArgumentException{ if(size <=0 || end+size>buffer.length){ + //不支持动态扩容 throw new IllegalArgumentException("invalid entity size "+size); } buffer[end] = entity; diff --git a/src/main/java/org/caoym/jjvm/runtime/StackFrame.java b/src/main/java/org/caoym/jjvm/runtime/StackFrame.java index b29f142..2f3c92d 100644 --- a/src/main/java/org/caoym/jjvm/runtime/StackFrame.java +++ b/src/main/java/org/caoym/jjvm/runtime/StackFrame.java @@ -29,6 +29,11 @@ public class StackFrame { */ private OpcodeInvoker[] opcodes; + /** + * 字节码 &操作数 + */ + private OpcodeInvoker[] ops; + /** * 程序计数器 */ @@ -45,10 +50,12 @@ public class StackFrame { StackFrame(JvmClass clazz,JvmMethod method, ConstantPool constantPool, OpcodeInvoker[] opcodes, + OpcodeInvoker[] ops, int variables, int stackSize) { this.constantPool = constantPool; this.opcodes = opcodes; + this.ops = ops; this.operandStack = new SlotsStack<>(stackSize); this.localVariables = new Slots<>(variables); this.clazz = clazz; @@ -58,6 +65,9 @@ public class StackFrame { public Slots getLocalVariables() { return localVariables; } + public void setLocalVariables() { + + } public SlotsStack getOperandStack() { return operandStack; @@ -91,13 +101,22 @@ public boolean isReturned() { public int getPC() { return pc; } + public int increasePC(){ return pc++; } + public int increasePC(int n) { + pc = pc + n; + return pc; + } + public OpcodeInvoker[] getOpcodes() { return opcodes; } - + public OpcodeInvoker[] getOps() { + return ops; + } + public JvmClass getCurrentClass() { return clazz; } diff --git a/src/main/java/org/caoym/samples/sample1/HelloWorld.java b/src/main/java/org/caoym/samples/sample1/HelloWorld.java new file mode 100644 index 0000000..4e15a1f --- /dev/null +++ b/src/main/java/org/caoym/samples/sample1/HelloWorld.java @@ -0,0 +1,9 @@ + +package org.caoym.samples.sample1; + +public class HelloWorld { + public static void main(String[] args){ + System.out.println("Hello World"); + System.out.println(args.length); + } +} diff --git a/src/main/java/org/caoym/samples/sample2/Main.java b/src/main/java/org/caoym/samples/sample2/Main.java new file mode 100644 index 0000000..1be0f0c --- /dev/null +++ b/src/main/java/org/caoym/samples/sample2/Main.java @@ -0,0 +1,11 @@ +package org.caoym.samples.sample2; + +public class Main{ + + private final static SpeakerInterface speaker = new Speaker("Hello"); + + public static void main(String[] args){ + System.out.println("arg length : "+ args.length); + speaker.helloTo(args[0]); + } +} diff --git a/src/main/java/org/caoym/samples/sample2/Speaker.java b/src/main/java/org/caoym/samples/sample2/Speaker.java new file mode 100644 index 0000000..f995b8e --- /dev/null +++ b/src/main/java/org/caoym/samples/sample2/Speaker.java @@ -0,0 +1,15 @@ +package org.caoym.samples.sample2; + +class Speaker implements SpeakerInterface { + private String hello = ""; + + Speaker(String hello){ + this.hello = hello; + } + + public void helloTo(String somebody) { + System.out.println(this.hello +" "+ somebody); + //System.out.println(this.hello); + //System.out.println(somebody); + } +} diff --git a/src/main/java/org/caoym/samples/sample2/SpeakerInterface.java b/src/main/java/org/caoym/samples/sample2/SpeakerInterface.java new file mode 100644 index 0000000..7815e4e --- /dev/null +++ b/src/main/java/org/caoym/samples/sample2/SpeakerInterface.java @@ -0,0 +1,5 @@ +package org.caoym.samples.sample2; + +public interface SpeakerInterface { + public void helloTo(String somebody); +} diff --git a/src/main/java/org/caoym/samples/sample3/HelloWorld.class b/src/main/java/org/caoym/samples/sample3/HelloWorld.class new file mode 100644 index 0000000..f523719 Binary files /dev/null and b/src/main/java/org/caoym/samples/sample3/HelloWorld.class differ diff --git a/src/main/java/org/caoym/samples/sample3/HelloWorld.java b/src/main/java/org/caoym/samples/sample3/HelloWorld.java new file mode 100644 index 0000000..3e7d8d1 --- /dev/null +++ b/src/main/java/org/caoym/samples/sample3/HelloWorld.java @@ -0,0 +1,15 @@ + +package org.caoym.samples.sample3; + +public class HelloWorld { + public static void main(String[] args){ + System.out.println("Hello World"); + int a = 1; + int b = 2; + if(a>b){ + System.out.println("a>b is true"); + }else{ + System.out.println("a<=b is true"); + } + } +} diff --git a/src/main/java/org/caoym/samples/sample4/RunnableDemo.java b/src/main/java/org/caoym/samples/sample4/RunnableDemo.java new file mode 100644 index 0000000..6e56bdd --- /dev/null +++ b/src/main/java/org/caoym/samples/sample4/RunnableDemo.java @@ -0,0 +1,33 @@ +package org.caoym.samples.sample4; + +class RunnableDemo implements Runnable { + private Thread t; + private String threadName; + + RunnableDemo( String name) { + threadName = name; + System.out.println("Creating " + threadName ); + } + + public void run() { + System.out.println("Running " + threadName ); + try { + for(int i = 4; i > 0; i--) { + System.out.println("Thread: " + threadName + ", " + i); + // 让线程睡眠一会 + Thread.sleep(50); + } + }catch (InterruptedException e) { + System.out.println("Thread " + threadName + " interrupted."); + } + System.out.println("Thread " + threadName + " exiting."); + } + + public void start () { + System.out.println("Starting " + threadName ); + if (t == null) { + t = new Thread (this, threadName); + t.start (); + } + } +} diff --git a/src/main/java/org/caoym/samples/sample4/TestThread.java b/src/main/java/org/caoym/samples/sample4/TestThread.java new file mode 100644 index 0000000..eb60472 --- /dev/null +++ b/src/main/java/org/caoym/samples/sample4/TestThread.java @@ -0,0 +1,12 @@ +package org.caoym.samples.sample4; + +public class TestThread { + + public static void main(String args[]) { + RunnableDemo R1 = new RunnableDemo( "Thread-1"); + R1.start(); + + RunnableDemo R2 = new RunnableDemo( "Thread-2"); + R2.start(); + } +} diff --git "a/\351\242\235\345\244\226\350\265\204\346\226\231-DIY\344\270\200\344\270\252GC(\346\234\252\345\256\214).md" "b/\351\242\235\345\244\226\350\265\204\346\226\231-DIY\344\270\200\344\270\252GC(\346\234\252\345\256\214).md" new file mode 100644 index 0000000..684b41c --- /dev/null +++ "b/\351\242\235\345\244\226\350\265\204\346\226\231-DIY\344\270\200\344\270\252GC(\346\234\252\345\256\214).md" @@ -0,0 +1,148 @@ +# 原文信息 + + 地址 https://shipilev.net/jvm/diy-gc/#_epsilon_gc + + 感谢 + ``` + Aleksey Shipilёv, JVM/Performance Geek, redhat logo + Shout out at Twitter: @shipilev + Questions, comments, suggestions: aleksey@shipilev.net + + This post is also available in ePUB and mobi. + Thanks to Richard Startin, Roman Kennke, Cyrille Chépélov and others for reviews, edits and helpful suggestions! + ``` + +# 正文 + +## 简介 + + 构建任何语言的 runtime 部分,都是一次有趣的练习。至少,在第一次时是如此。但如果要建立一个可靠的、高性能的、易观察和调试、容易查错的runtime系统,则是一件非常挑战的事情。 + + 建立一个简单 GC 看起来很简单,我们将在这里进行一次。Roman Kennke 在 2019年度 FOSDEM (自由及开源软件开发者欧洲会议,Free and Open source Software Developers' European Meeting,简称FOSDEM) + 发表了一次演讲并展示了一个 "如何在20分钟内开发一个GC" 的 demo,他使用了一个早期版本的 GC。实际代码附有很多说明,本文来自于其中一部分。 + + 如果对 GC 有一定了解,会对阅读本文很有帮助。这里一些关于概念和Hotspot Specifics 的内容,但更深入的内容请参看 [GC Handbook](http://gchandbook.org/) 的第一章,也可以参看 [Wikipedia article](https://en.wikipedia.org/wiki/Tracing_garbage_collection) + +### 1.建立 Blocks + + 参照着已有的 GC 实现再做一个新的 GC,有现成可借用/参考的部分,容易很多。 + +#### 1.1. Epsilon GC + + [JEP 318: "Epsilon: A No-Op Garbage Collector (Experimental)" ](https://openjdk.java.net/jeps/318)在 OpenJDK 11 被引入。目的是提供一个最小实现,用于对回收没有需要/被禁用 的场景。更多信息请参看 JEP。 + + 从这个实现的角度,GC 这个用词不太恰当,用“自动化内存管理”更合适。它包含内存分配和回收两部分。Epsilon GC 只包含其中的内存分配功能,没有涉及回收部分,因此在它之上建立实际的 GC 会比较容易。 + +##### 1.1.1. 分配 + + Epsilon GC 最完善的部分是[分配地址的处理](http://hg.openjdk.java.net/jdk/jdk/file/6c96d42ec3e7/src/hotspot/share/gc/epsilon/epsilonHeap.cpp#l173)。 + 它给请求方提供强制对齐后的内存块,或按请求的size分配 TLAB[Thread-Local Allocation Buffer](https://shipilev.net/jvm/anatomy-quarks/4-tlab-allocation/)。 + 实现本身没对 TLABs 做太多扩充,主要因为没有回收机制。 + +##### 1.1.2. 内存屏障 + + 有些 GC 强制要求 runtime 和 应用 + + Epsilon 不需要内存屏障,但 runtime 和 编译器 仍需要知道它是必选的。从前插入内存屏障的实现很琐碎,从 OpenJDK 11开始,根据[JEP-304: "Garbage Collection Interface"](https://openjdk.java.net/jeps/304),变得简单多了。 + 需要留意的是,Epsilon 没有实现内存屏障,它将所有工作,包括load、store、CAS、arraycpoy,都委托给基础的内存屏障。如果我们要构建一个不需要内存屏障的GC,直接重用Epsilon的就好。 + +##### 1.1.3. 监控用的钩子 + + GC中最后一个细碎繁琐的部分是 JVM 监控,例如 MX bean、诊断命令支持等,Epsilon在[这里](http://hg.openjdk.java.net/jdk/jdk/file/tip/src/hotspot/share/gc/epsilon/epsilonMonitoringSupport.cpp)提供了实现。 + +#### 1.2 Runtime 和 GC + +##### 1.2.1 根节点 + + GC 需要知道哪些 Heap 上的对象被引用。这要通过 根节点 来判断,包括线程栈、本地变量(包括 JIT 生成的代码)、JNI类和classloader、JNI handle等。 + +##### 1.2.2. 对象遍历 + + GC 也需要遍历Java对象。但因为这是常见功能,因此 GC 的公共实现部分已经提供了。 + 具体可参看文中后续部分 obj->oop_iterate。 + +##### 1.2.3 移动对象 + + GC 需要记录被移动对象的新地址。主要有以下几种方式: + + 1.复用对象的"mark word"字段(Serial, Parallel 等算法) + 当 STW 时,所有对象访问都受控,Java Thread对该字段也不可见。因此可复用该字段。 + 2.维护独立移动表(ZGC, C4 等算法) + 该方式会将 GC 同 runtime/其他应用 完全隔离,只有 GC 能感知 移动表 的存在。 + 这也是为何 并发 GC 经常使用该方式。 + 3.Java对象增加额外字段记录(Shenandoah 等算法) + [Shenandoah](http://openjdk.java.net/jeps/189),结合以上两种方式:使用 runtime/应用 已有的头部字段,同时用额外字段记录了移动字段的信息。 + +##### 1.2.4 标记对象 + + GC 需要记录可达的对象,主要有以下几种方式: + + 1.复用对象的"mark word"字段(Serial, Parallel 等算法) + +### 2. Grand Plan + + +### 3. Implementing GC Core + + 完整实现在[这里](https://shipilev.net/jvm/diy-gc/webrev/),本文逐段来说。 + +#### 3.1 序言 + +``` + { + GCTraceTime(Info, gc) time("Step 0: Prologue", NULL); + + // 提交标记 bitmap ,好处是: + // - 如果没 GC 不消耗内存 + // - bitmap不可达的部分,会map到0页,以提高处理heap的效率 + if (!os::commit_memory((char*)_bitmap_region.start(), _bitmap_region.byte_size(), false)) { + log_warning(gc)("Could not commit native memory for marking bitmap, GC failed"); + return; + } + + // 本算法不会解析heap,但希望回收 thread 的 TLAB + ensure_parsability(true); + + // 告诉 runtime 要做 GC 了 + CodeCache::gc_prologue(); + BiasedLocking::preserve_marks(); + + // 清理 派生指针 + DerivedPointerTable::clear(); + } +``` + +#### 3.2. 标记 + + 如果做好标记,STW 将很简单。所以标记也是很多 GC 算法实现的第一步骤。 + +``` + { + GCTraceTime(Info, gc) time("Step 1: Mark", NULL); + + // 标记栈和闭包会处理大部分工作。 + // - 闭包会扫描出废弃的引用、标记,并将最近标记的对象压入栈,以便后续处理 + EpsilonMarkStack stack; + EpsilonScanOopClosure cl(&stack, &_bitmap); + + // 标记根节点 + process_roots(&cl); + stat_reachable_roots = stack.size(); + + // 扫描heap剩余部分 + while (!stack.is_empty()) { + oop obj = stack.pop(); + obj->oop_iterate(&cl); + stat_reachable_heap++; + } + + // 标记完成后,所有派生指针都被找到 + DerivedPointerTable::set_active(false); + } +``` + + + + + + \ No newline at end of file