JVM中的JIT编译器到底是什么?

5
我想了解Java源代码如何执行,但对JVM内部的JIT编译器感到困惑。让我先告诉您从Java源代码到在计算机上执行机器代码的过程是如何理解的。也许,在这个过程中我误解了一些东西,导致了困惑。
以下是步骤:
1. 源代码被编译成字节码(.class文件) 2. 类文件被加载到JVM(位于RAM中) 3. 字节码被验证,然后由JIT编译器处理 4. JIT编译器的输出是准备好执行的机器代码
根据Wikipedia关于JVM的文章,更具体地说是"Bytecode interpreter and just-in-time compiler"章节,为了执行Java字节码,需要一个解释器(但我们有一个JIT编译器)。
现在这里有一点令我困惑。我将其分解为引用:
"When Java bytecode is executed by an interpreter, the execution will always be slower than the execution of the same program compiled into native machine language."
  1. 由于计算机只能执行机器码,解释器在将字节代码转换为机器码方面比编译器慢,那么为什么JVM使用解释器而不是编译器?

  2. 为什么我们没有另一个由JIT编译器为CPU生成的中间可执行文件,以便快速执行指令?

"JIT编译器可以在执行程序时将Java字节码转换为本地机器语言。程序的已翻译部分可以比解释执行快得多。该技术应用于频繁执行的程序部分。"

JIT编译器真的是一种具有编译经常执行代码能力的解释器吗?编译器和解释器这两个术语是否被错误地交替使用了?

提前致谢。


1
“JIT编译器”这个术语已经过时了。它指的是1.3之前的JVM插件架构,可以在执行之前编译所有字节码。人们发现“JIT编译器会将代码随处散布”,这导致了我们现在所拥有的“HotSpot”,它根据其执行历史有选择地编译字节码。根据您提供的维基百科引用,当前的趋势是使用“JIT”来同时指代两者,但实际上这只会让人感到困惑,而且历史上也不准确。 - user207421
@EJP 谢谢您的回复。当您说它指的是两者时,是否指编译器和解释器?所以如果我理解正确,在JVM内部的翻译器最初是编译器,但由于安全问题而被替换。这意味着在1.3之前,从源代码到机器代码的整个翻译过程有2个单独的编译步骤。一个是从源代码到字节码的转换,另一个是从字节码到机器代码的转换。目前,翻译器更像是一种逐行执行代码并偶尔作为常用代码的编译器的解释器。正确吗? - ribarcheto94
我还想知道的另一件事是机器码是如何到达CPU的。我假设当代码逐行处理时,解释器只是传递那个机器码。不太明显的是,当你有一个包含机器码的中间文件时,CPU如何获取机器码。编译器是否会从文件中提供指令给CPU? - ribarcheto94
我的意思是,你引用的维基百科文章将JIT编译器和HotSpot JVM称为同一物,但它们相差甚远。我曾试图编辑过,但被禁止了。在1990年代末期,JIT DLL由第三方(例如Symantec)提供,并通过命令行选项启用到JVM中,这也回答了你上面的另一个问题;而HotSpot则内置于JVM中,并通过命令行选项禁用。在JIT和HotSpot下都没有包含机器代码的中间文件。 - user207421
2个回答

5
由于计算机只能执行机器码,而解释器在将字节码翻译成机器码方面比编译器慢,为什么JVM使用解释器而不是编译器?
因为编译成机器码也需要时间,特别是当它必须分析代码以进行优化时,因此解释器足够快以执行大部分时间,并且如果仅运行一次/偶尔运行,则实际上比编译+运行更快。
另外,解释器并不是“将字节码翻译成机器码”。它评估字节码并执行字节码请求的操作。解释器本身是机器码,但它不会翻译字节码,而是解释/评估字节码。
为什么JIT编译器不生成另一个中间可执行文件供CPU快速执行指令?
那将违反Java的“一次编写,到处运行”的范例。
JIT编译器是否真的是具有编译频繁执行的代码的解释器?
不,JIT编译器(或更准确地说,HotSpot编译器,正如EJP所提到的)是由JVM根据需要执行的编译器。
“编译器”和“解释器”这两个术语是否被错误地交替使用了?
正确。它们不能互换使用,因为它们不执行相同的操作。解释器执行字节码。JIT/HotSpot编译器将字节码转换为机器码,但不运行它。

笑!如果不需要解释,为什么JVM的维基百科文章会说:“对于每种硬件架构,都需要不同的Java字节码解释器。当计算机拥有Java字节码解释器时,它可以运行任何Java字节码程序,并且相同的程序可以在任何具有这样的解释器的计算机上运行。”根据您上面的评论,似乎解释器只是机器代码指令的包装器,对吗?如果编译语言不需要解释,那么编译后的代码如何执行? - ribarcheto94
@ribarcheto94 因为他们选择不将所有内容编译成机器代码,所以需要解释器来运行非编译代码。如果JVM实现选择将所有内容编译成机器代码,那么实现将不需要解释器。这完全取决于实现。拥有解释器并非强制性要求,但我认为没有任何以这种方式工作的实现。这并不意味着不能有。--- 结束我在#1中说的长时间重复。 - Andreas
@ribarcheto94 你似乎还不明白解释器是什么,以及它的作用。而代码是否为“编译语言”并不重要。如果代码被编译成机器码,则不需要解释器,因为CPU可以直接运行机器码。如果代码没有被编译,或者它被编译成某个中间格式(例如字节码),则需要一个解释器来运行代码。除非把中间代码进一步编译成机器码,那么解释器就再次不需要来运行代码。请参见https://en.wikipedia.org/wiki/Interpreter_(computing)。 - Andreas
@ribarcheto94 CPU运行机器码。这就是CPU的工作。 - Andreas
抱歉,如果你拒绝真正了解解释器的工作原理,那是你的决定,你永远不会学到东西。我给了你一个链接,请阅读它。你也可以在网上搜索。我已经解释完了。 - Andreas
显示剩余13条评论

2
由于计算机只能执行机器码,而解释器翻译字节码到机器码的速度比编译器慢,那么为什么JVM使用解释器而不是编译器呢?
优化编译是一个持续时间很长的过程。只有当程序运行更长时间时,这种开销才有意义。
错误的优化是不必要的。一段只遍历一次的代码将消耗比解释更多的编译时间,因此解释是可以接受的。
这两个问题最好由编译器处理,如果处理频繁,则编译器就会介入处理某些部分的代码。
为什么我们没有另一个中间可执行文件由JIT编译器生成给CPU,以便它可以快速执行指令?
这个“文件”(编译片段)确实存在于内存中。它不被序列化为文件,因为:
这样的文件将严重依赖于操作系统和硬件
有一些代码优化只能在运行时应用,例如JIT-编译器可以优化虚拟调用(通过跳转或甚至内联代码替换动态分派)
JIT编译器是否真正是具有编译频繁执行代码的能力的解释器?编译器和解释器这些术语是否被错误地交替使用?
虽然JVM Hotspot编译器编译频繁执行的代码,但其他JIT编译器可能会根据其他启发式方法决定编译。术语“JIT编译器”和“解释器”并不清楚地区分。大多数解释器会优化(即时编译),几乎每个JIT编译器都会解释。

“长久的”这个描述并不准确,它相对于运行应用程序的 JVM 并不是长久的。 - user207421
这是一般关于预先优化编译的声明。整个部分应该解释即时编译在预先编译之上的好处。因此,如果我们就事实达成一致,我可以编辑帖子使其更简洁明了。 - CoronA

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