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会将所有数组指令转换为函数(方法)调用。

特殊寄存器

目前存在多个特殊寄存器

  1. 返回值寄存器
  2. 异常捕获寄存器(和返回值寄存器重叠)
  3. Frame索引寄存器(虚拟机内的程序无法访问)
  4. Frame内代码偏移寄存器(虚拟机内的程序无法访问)

不存在WIDE指令

所有指令的参数均为32位宽(不同于寄存器长度,部分指令为2个、3个32位指令长度),后续可能进行指令压缩等方法。

定长指令

不同于JVMSpec存在部分指令为模糊的操作数长度(tableswitch,lookupswitch),存在模糊的栈操作数量(invoke*)。
GingerbreadVM中所有指令均有固定的操作数长度和固定的寄存器操作数量。
invoke通过修改函数(方法)调用方式实现,tableswitch,lookupswitch通过翻译为if-else实现(性能较低)。

功能性演示

1、截获数组操作实时获取排序信息