当前位置: 首页 > 面试题库 >

为什么编译的Java类文件比C的编译文件小?

徐飞龙
2023-03-14
问题内容

我想知道为什么我们通过编译显示“ Hello,World!”的.c文件得到.o文件。是否大于Java .class文件,该文件也显示“
Hello,World!”?


问题答案:

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命令)一起使用。如您所见,机器代码充满了寄存器,与更简单的基于堆栈的字节码相比,它为编译的程序提供了更大的空间。



 类似资料:
  • 问题内容: 如何批量反编译许多类文件? 问题答案: JD-Gui使您的生活变得轻松,它还具有一个eclipse插件 编辑:更新了该工具的最新网址

  • 问题内容: 我可以使用什么程序反编译类文件?我实际上会得到Java代码,还是仅仅是JVM汇编代码? 关于此站点上的Java性能问题,我经常看到“反编译” Java类文件以了解编译器如何优化某些东西的人们的回答。 问题答案: 有一些反编译器…快速搜索结果: Procyon: open-source (Apache 2) and actively developed Krakatau: open-so

  • 我写了下面提到的一个简单的java程序。不幸的是,编译错误发生了。 在编译过程中,在命令提示符下会显示以下内容: c:\Java 它对我的任何程序都不起作用,即使是这个简单的程序也不行!这是为什么? 编辑: 现在我有: 而且它不起作用。为什么不起作用? 它说的和以前一样。 你对Java所说的。lang.String[]有效,但为什么不能呢?为什么我以前不需要穿上这些?

  • 问题内容: 我保存了Java源文件,将其编码类型指定为UTF-8(使用记事本,默认情况下,记事本的编码类型为ANSI),然后尝试使用以下命令对其进行编译: 但它给出了错误信息” 还有什么其他方法可以编译吗? 来源如下: 问题答案: 您的文件 被 读为UTF-8,否则有值“65279”字符就绝不可能出现。期待你的源代码是在平台默认的编码,根据该文件: 如果未指定 -encoding ,则使用平台默认

  • 问题内容: 您好,我有3个Java文件 我设法使用生成了A和B的.class文件 但是当我对c.java执行相同操作时,出现错误错误:找不到符号b和c 关于如何解决此问题的任何建议? 所有的Java文件都在同一个文件夹中 问题答案: 尝试编译class时,必须具有类并且在类路径中。这使编译器可以验证它们是否存在,找出它们具有哪些方法等。 对包名称和类路径非常敏感。最简单的方法是像这样同时编译这三个

  • 我已经编译了我的。proto文件使用protobuf编译器并收到了一组Java文件。我收到了一份原始文件。java文件和。中每个项目的java文件。proto文件,包括消息类型和每个RPC调用,例如publicKeyRequest。java和Quote。java作为RPC和请求参数类型。 这是所有需要的文件吗,因为我似乎仍然无法从服务器获得任何简单的响应? 我想为PublicKeyRequest