关于编译器构造:字节码解析指令和机器语言之间的区别?

Difference between a bytecode parsed instruction and machine language?

"A bytecode program is normally executed by parsing the instructions one at a time. This kind of bytecode interpreter is very portable. Some systems, called dynamic translators, or"just-in-time" (JIT) compilers, translate bytecode into machine language as necessary at runtime: this makes the virtual machine unportable."

关于这一段的问题是:在处理字节码之后,
解析指令和机器语言(或机器代码)之间的区别是什么?


JIT与字节码解释器不同。

考虑以下C函数:

1
2
3
int sum() {
   return 5 + 6;
}

这将直接编译机器代码。关于x86和ARM处理器的确切说明将有所不同。

如果我们编写了一个基本的字节码解释器,它可能看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
for(;;) {
   switch(*currentInstruction++) {
   case OP_PUSHINT:
      *stack++ = nextInt(currentInstruction);
      break;
   case OP_ADD:
      --stack;
      stack[-1].add(*stack);
      break;
   case OP_RETURN:
      return stack[-1];
   }
}

然后,这可以解释以下一组说明:

1
2
3
4
OP_PUSHINT (5)
OP_PUSHINT (6)
OP_ADD
OP_RETURN

如果您在x86或ARM上编译了字节码解释器,那么您将能够运行相同的字节代码而无需进一步重写解释器。

如果您编写了JIT编译器,则需要为每个支持的处理器发出特定于处理器的指令(机器代码),而字节代码解释器依赖于C ++编译器来发出处理器特定的指令。


在字节码解释器中,指令格式通常用于使用移位和掩码运算符进行非常快速的"解析"。解释器在"解析"(我更喜欢"解码")指令之后,立即更新虚拟机的状态,然后开始解码下一条指令。因此,在解释器中处理字节码后,没有剩余的残余。

在JIT编译器中,字节以大于单个指令的单位进行处理。最小单位是基本块,但现代JIT将更大的路径转换为机器代码。这是转换步骤,转换步骤的输出是机器代码。原始字节码可能保留在内存中,但它不用于实现—因此没有真正的区别。 (尽管JITted虚拟机的机器代码与本机代码编译器发出的机器代码完全不同,但通常也是如此。)


归根结底,它归结为机器指令。

  • Native App - 包含直接执行的机器指令。
  • JIT App - 将字节码编译成机器指令并执行。
  • 翻译的应用程序 - 字节码由作为本机应用程序的虚拟机翻译。
  • 正如您所知,使用#1,您拥有最少的开销,而使用#3,您的开销最大。因此,在初始编译开销之后,性能应该在#1上最快,在#2上同样快。


    没有区别 - JIT编译器正是为此完成的 - 它产生了在硬件上执行的机器代码。