Java和.Net的JIT有什么不同?

15

我知道 Microsoft .NET 使用 CLR 作为 JIT 编译器,而 Java 则使用 Hotspot。它们之间有什么区别呢?


2
NGen 用于将 MSIL 预编译为本机代码,并对整个程序集执行此操作,但是当您不使用 NGen 时,.Net CLR 中的 JIT 编译器会在运行时为您执行此操作。最好使用 CLR 中的 JIT 编译器进行比较,而不是使用 NGen vs Hotspot 进行比较。 - saret
1个回答

26

它们是非常不同的东西。正如人们指出的那样,CLR在执行MSIL代码之前编译成机器码。这使得除了典型的死代码消除和内联私有优化外,它还可以利用目标机器的特定CPU架构(尽管我不确定它是否这样做)。这也会为每个类带来一些负面影响(尽管编译器相当快速,许多平台库只是Win32 API的薄层包装)。

HotSpot VM采取了不同的方法。它认为大部分代码很少被执行,因此不值得花费时间进行编译。所有字节码都以解释模式开始。VM在调用点保留统计信息,并尝试识别被调用超过预定义次数的方法。然后,它仅使用快速JIT编译器(C1)编译这些方法,并在运行时交换方法(这就是HS的独特之处)。在C1编译的方法被调用多次后,同一方法会用缓慢但复杂的编译器进行编译,并再次交换代码。

由于HotSpot可以在方法运行时交换方法,所以VM编译器可以执行一些在静态编译代码中不安全的推测优化。一个典型的例子是静态分派/单态调用(只有一种实现的多态方法)的内联。如果VM看到该方法总是解析到同一个目标,那么就会执行此操作。曾经复杂的调用被简化为几个CPU指令,由现代CPU进行预测和流水线处理。当保护条件不再成立时,VM可以采取不同的代码路径或甚至回退到解释模式。根据统计数据和程序工作负载,在不同时间生成的机器代码可能不同。这些优化中的许多依赖于程序执行期间收集的信息,如果你在加载类时只编译一次,则无法实现这些优化。

这就是为什么在基准测试算法时需要预热JVM并模拟现实工作负载(偏斜数据可能导致对优化的不切实际的评估)。其他优化包括锁消除、自适应自旋锁、逃逸分析和堆栈分配等。

尽管如此,HotSpot只是众多虚拟机之一。JRockit、Azul、IBM的J9和可重置RVM - 都有不同的性能特征。


1
Java中的“所有字节码都以解释模式启动”是真的吗?我认为在启动时还会进行调用计数,如果超过阈值,它们将在启动时编译。这可能取决于编译器:https://www.ibm.com/support/knowledgecenter/en/SSYKE2_8.0.0/com.ibm.java.lnx.80.doc/diag/understanding/jit_overview.html - kovac
@kovac - 对于OpenJDK Hotspot,这是正确的。所有代码都从C1开始,只有热循环/方法才会由C2编译。然而,Java语言规范并没有要求每个JVM都应该这样做。 - JohnTortugo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接