小编典典
Java使用字节码来独立于平台并进行“预编译”,但是字节码由解释器使用并且被提供为足够紧凑,因此您在已编译的C程序中看到的机器代码并不相同。只需看一下Java编译的完整过程即可:
Java program
-> Bytecode
-> High-level Intermediate Representation (HIR)
-> Middle-level Intermediate Representation (MIR)
-> Low-level Intermediate Representation (LIR)
-> Register allocation
-> EMIT (Machine Code)
这是Java程序到机器代码转换的链。如您所见,字节码与机器代码相距甚远。我在Internet上找不到能在实际程序上向您展示这条路的东西(一个示例),我发现的只是此演示文稿,在这里您可以看到每个步骤如何更改代码演示文稿。我希望它能回答您如何以及为何编译的c程序和Java字节码不同。
更新:
“字节码”之后的所有步骤都由JVM在运行时完成,这取决于其编译该代码的决定(这是另一回事了……JVM在字节码解释和其编译成与本地平台相关的代码之间取得平衡)
最终找到了一个很好的例子,摘自Java
HotSpot™客户端编译器的线性扫描寄存器分配(很好地理解了JVM内部的情况)。想象一下,我们有Java程序:
public static void fibonacci() {
int lo = 0;
int hi = 1;
while (hi < 10000) {
hi = hi + lo;
lo = hi - lo;
print(lo);
}
}
那么它的字节码是:
0: iconst_0
1: istore_0 // lo = 0
2: iconst_1
3: istore_1 // hi = 1
4: iload_1
5: sipush 10000
8: if_icmpge 26 // while (hi < 10000)
11: iload_1
12: iload_0
13: iadd
14: istore_1 // hi = hi + lo
15: iload_1
16: iload_0
17: isub
18: istore_0 // lo = hi - lo
19: iload_0
20: invokestatic #12 // print(lo)
23: goto 4 // end of while-loop
26: return
每个命令占用1个字节(JVM支持256个命令,但实际上少于该数目)+参数。总共需要27个字节。我省略了所有阶段,现在可以执行机器代码了:
00000000: mov dword ptr [esp-3000h], eax
00000007: push ebp
00000008: mov ebp, esp
0000000a: sub esp, 18h
0000000d: mov esi, 1h
00000012: mov edi, 0h
00000017: nop
00000018: cmp esi, 2710h
0000001e: jge 00000049
00000024: add esi, edi
00000026: mov ebx, esi
00000028: sub ebx, edi
0000002a: mov dword ptr [esp], ebx
0000002d: mov dword ptr [ebp-8h], ebx
00000030: mov dword ptr [ebp-4h], esi
00000033: call 00a50d40
00000038: mov esi, dword ptr [ebp-4h]
0000003b: mov edi, dword ptr [ebp-8h]
0000003e: test dword ptr [370000h], eax
00000044: jmp 00000018
00000049: mov esp, ebp
0000004b: pop ebp
0000004c: test dword ptr [370000h], eax
00000052: ret
结果需要83个字节(十六进制52个+ 1字节)。
PS。我没有考虑链接(其他人提到过),也没有考虑编译的c和字节码文件头(可能也有所不同;我不知道c怎么回事,但是在字节码文件中,所有字符串都移到了特殊的标头池,并且在程序中在标头等处使用其“位置”。)
UPDATE2: 也许值得一提的是,尽管基于x86和大多数其他平台的机器代码可以与寄存器一起使用,但是java与堆栈(istore /
iload命令)一起使用。如您所见,机器代码充满了寄存器,与更简单的基于堆栈的字节码相比,它为编译的程序提供了更大的空间。
2020-11-01