GingerbreadVM_V0.1目前状态
在探索OpenJDK时,发现了JVMSpec对java程序处理的很多特性。
这些特性在我认为在JIT模式盛行的现在,这些特性仅会保留到JVM将代码翻译为机器码前。
故GingerbreadVM一定程度上与OpenJDK的实现并不相同。
同时受限于精力的影响,仍有许多功能未进行实现。
指令支持情况
指令 | 支持情况 | 目前行为 |
---|---|---|
invokedynamic ret jsr jsr_w |
不支持 | 报错 |
monitorenter monitorexit |
不支持 | 不报错,但不产生正常行为 |
其他 | 支持 | 正常运行 |
暂时不支持的功能
注解功能
动态调用与方法句柄
暂不支持所有lambda。
垃圾回收
目前完全不支持垃圾回收,因为目前很少涉及内存分配。
desktop,io等与本地相关的组件
其他类加载器
异常捕获时代码行数
特性
类加载顺序可能错误
目前类加载依赖方式可能不正常,部分类可能不按照正常行为运行。
指令运行方式与OpenJDK存在很大差异
通过转换字节码,过滤掉有可替换关系的字节码
例如 ICONST_*, LCONST_*,BIPUSH,SIPUSH,ACONST_NULL,LDC 所有从常量区加载到寄存器的行为均会翻译为类LDC命令。
POP均会翻译为NOP(因为没有实际的寄存器值变化)。
DUP均会翻译为MOV指令(MOV虽翻译为移动,但行为均为复制)。
运行方式不同于JVM的堆栈+本地变量
使用的是线性内存,不需要支持堆栈运行模式。原则上也允许寄存器重命名进行指令优化。
寄存器长度为64位
堆栈存在long,double时,无需跳过一个寄存器。
常量区存在long,double时无需跳过寄存器。
使用特殊结构Frame
合并了StackMapTable(堆栈映射表子结构),Exception(方法异常表捕获表)。
同一个Frame内不允许有差异的堆栈表或捕获表。
函数(方法)调用方式差异
不同于JVMSpec使用invoke*来调用4种方法,GingerbreadVM使用四个分离的指令完成。
graph TD; START(( 开始 )) A("向VM发起创建新堆栈的请求,同时声明调用参数的数量") B("向VM发起将特定寄存器的值推入调用寄存器") E("准备寄存器数据") C("发起函数调用") D("从返回值寄存器获取数据") END(( 结束 )) START --> A -->E--> B --当所有变量均推入调用寄存器后--> C --如果回参不是void--> D --> END; B --直到所有参数推入调用寄存器--> E; C--如果回参是void-->END;
捕获方式不同
不同于JVMSpec自动将捕获的异常推入栈顶,GingerbreadVM需要手动从异常捕获寄存器
中获取数据。
对数组的指令实现方式不同
不同于JVMSpec定义的数组指令,GingerbreadVM会将所有数组指令转换为函数(方法)调用。
特殊寄存器
目前存在多个特殊寄存器
- 返回值寄存器
- 异常捕获寄存器(和返回值寄存器重叠)
- Frame索引寄存器(虚拟机内的程序无法访问)
- Frame内代码偏移寄存器(虚拟机内的程序无法访问)
不存在WIDE指令
所有指令的参数均为32位宽(不同于寄存器长度,部分指令为2个、3个32位指令长度),后续可能进行指令压缩等方法。
定长指令
不同于JVMSpec存在部分指令为模糊的操作数长度(tableswitch,lookupswitch),存在模糊的栈操作数量(invoke*)。
GingerbreadVM中所有指令均有固定的操作数长度和固定的寄存器操作数量。
invoke通过修改函数(方法)调用方式实现,tableswitch,lookupswitch通过翻译为if-else实现(性能较低)。