为什么在使用JIT编译器的同时,JVM还要使用解释器?

huangapple go评论73阅读模式
英文:

Why Interpreter is used by JVM when JIT compiler is also used?

问题

我们知道,JVM同时使用解释器和JIT编译器。JIT编译器将那些重复的字节码转换为机器码,并将它们存储在内存中。现在,当解释器逐行翻译字节码并运行时,对于已经转换并存储在内存中的重复代码,它将直接跳过翻译部分,而直接运行它。从而减少并发冗余的翻译。

那么为什么Java在JVM中使用解释器?一个像JIT这样的编译器本可以一次性完成将字节码转换为机器码的整个任务吗?

英文:

We know, JVM uses both interpreter and JIT compiler. JIT compiler converts those Byte Codes that are repeated into Machine Code and stores them in memory. Now when the Interpreter is translating the ByteCode line-by-line and running it, it will simply skip the translation part for a repeated code already converted and stored in memory but will run it directly. Thus reducing the concurrent redundant translation.

Then why Java uses an interpreter in JVM? A compiler like JIT could have done the whole task of converting Byte Code to Machine Code at once?

答案1

得分: 1

并非所有的JVM实现都采用解释器和JIT编译器的组合。有些只有解释器,有些只有JIT编译器,还有些有两者。

最著名的JVM是HotSpot JVM,它确实有两个解释器和两个JIT编译器:

  • 解释器使用可移植的C++编写。优势在于您的系统只需要一个C++编译器就可以运行这个解释器。
  • "模板化"解释器使用某种汇编语言编写。这个解释器依赖于体系结构。
  • 客户端编译器:编译速度快,生成的代码效率较低。
  • 服务器端编译器:编译速度较慢,生成高度优化的代码。

根据您的系统或如何配置HotSpot,它将包含C++解释器或模板解释器,这两者不能结合在一起(据我所知)。

这里我们有解释的第一个优势:它更具可移植性。许多硬件/操作系统组合都有C++编译器。JIT编译器会生成机器代码,因此必须分别针对每种支持的体系结构进行移植。

另一个优势是启动时间。JIT编译需要时间,但解释器可以立即开始执行代码。此外,JIT编译器可以在代码解释的同时在后台运行。

不仅JIT编译器可以在解释器运行时进行编译,而且它还可以生成返回解释器的代码。这意味着JIT编译器不必支持所有功能/边界情况(出于任何原因)。如果它遇到无法编译的内容,它可以从编译代码中生成跳转到解释器的指令,甚至可以放弃编译该代码 - 这没问题(在某种意义上),因为它仍然可以被解释执行。

在这种复杂系统中为某种方法提供确凿的证据是困难的。一个数据点可能是,其他被认为是先进技术水平的虚拟机也采用了与解释器和两个编译器相似的方法:V8和SpiderMonkey JavaScript虚拟机。

OP还问为什么不提前编译所有内容。首先,这可以通过GraalVM本地图像来实现,还有一些类似的技术。其次,不提前编译具有一些优势:您可以在编译代码之前运行一段时间,以观察其行为并更精确地定位JIT编译器中的优化(此if的else分支从未执行过-不要浪费内联预算),并且因为可以跳转到解释器,所以还可以进行“推测性”优化(甚至不需要编译else分支),其中如果推测失败,代码会跳回解释器。但这种方案不需要解释器。只需要一个可以从一种编译(更优化)跳转到另一种(更通用)的JIT编译器即可。JRockit就是这样做的(据我所知)。

英文:

Not all JVM implementations employ combination of an interpreter and a JIT compiler. Some have only interpreter, some only a JIT compiler or two.

The most well known JVM the HotSpot JVM does have two interpreters and JIT two compilers:

  • interpreter written in portable C++. The advantage is that you only need a C++ compiler on your system to run this interpreter
  • "templated" interpreter written in sort of assembly language. This interpreter is architecture dependent.
  • client compiler: fast compilation, less efficient code
  • server compiler: slower compilation, highly optimized code

Depending on your system or how you configure HotSpot it will contain either the C++ interpreter or the templated one, those two cannot be combined (afaik).

Here we have first advantage of interpretation: it is more portable. There is a C++ compiler for lots of hardware/OS combinations. The JIT compilers produce machine code and so have to be ported separately to each supported architecture.

Another advantage is startup time. JIT compilation takes time, but the interpreter can just start executing the code instantaneously. Moreover, the JIT compiler can just run on the background while the code is interpreted.

Not only the JIT compiler can compile while the interpreter is running, but it can also generate code that jumps back to the interpreter. This means that the JIT compiler does not have to support all the features/corner cases (for whatever reason). If it comes across something it cannot compile, it can generate a jump to the interpreter from the compiled code or it can even give up on compiling that code altogether - it's fine (in some sense), because it can still be interpreted.

Giving a hard evidence for some approach within such complex systems is, well, hard. One data-point could be that other VMs that are considered advanced state-of-the-art employ similar approach with interpreter and two compilers: V8 and SpiderMonkey JavaScript VMs.

OP also asks why not to compile everything ahead of time. Firstly, this can be done with GraalVM native image and there have been some other similar technologies. Secondly, not compiling ahead of time has some advantages: the thing that you can run the code for a while before you compile it means that you can observe how it behaves and target the optimizations in the JIT compiler more precisely (the else branch of this if was never executed - don't waste the inlining budget for it) and because you can jump to the interpreter, you can also do "speculative" optimizations (don't even bother compiling the else branch) where if the speculation fails, the code jumps back to the interpreter. This scheme does not need interpreter, however. Just a JIT compiler that can jump from one compilation (more optimized) to another (more generic) will do. JRockit did that (afaik).

答案2

得分: 0

现代的Java实现,比如GraalVM,实际上提供了将整个Java字节码类甚至整个应用程序编译成本机代码的选项,但是这是预先编译(ahead-of-time),而不是在运行时。理论上,完全可以实现一个在运行时处理整个类的JIT编译器,从而完全避免了解释的需要。

然而,在你等待GraalVM的本机代码编译器处理完毕的时候,已经足够吃一顿三道菜的时间了,很容易理解为什么批量预编译不是JVM中默认的编译机制。编译成本机代码可能会很慢,而且Java并不是一门天生适合编译的语言。

大多数JVM都提供了某种形式的自适应解释/编译平衡,在运行时实现。对于重复执行很多次的代码段,解释会变得“慢”,因为解释的工作会被重复多次执行。对于只执行一次的代码,编译会变得“慢”,因为编译的工作必须在实际执行任何程序指令之前完成。

因此,现代的Java实现提供了这两种策略,并尝试在运行时平衡它们,以获得最佳的整体性能。总体上,我们希望(JIT)只编译应用程序中那些编译时间成本最被性能收益所抵消的部分。如何实现这一点已经是大量研究的主题,新的方法仍在不断发展中。

英文:

Modern Java implementations like GraalVM do, in fact, offer the option to compile to native code entire classes of Java bytecode -- or even entire applications -- but ahead-of-time, not at runtime. It would certainly be possible for a JIT compiler to be implemented that processed an entire class at runtime, and thus avoided the need for interpretation at all.

But, after you've had time for a three-course meal while GraalVM's native-code compiler does it's stuff, it's easy enough to understand why bulk pre-compilation isn't the default compilation mechanism in JVMs. Compilation to native code can be slow, and Java isn't an inherently compilable language.

Most JVMs offer some form of adaptive interpretation/compilation balance, implemented at runtime. For code sections that are repeated a great deal, interpretation is "slow", because the work of interpretation is repeated many times. For code that is executed only once, compilation is "slow", because the work of compilation has to be done before any program instructions are actually executed.

So modern Java implementations offer both strategies, and attempt to balance them to get the best overall performance at runtime. Broadly, what we'd like to do is to (JIT) compile only those parts of the application where the time cost of compilation is most offset by the benefits it brings. How to do this has been the subject of a good deal of research, and new methods continue to be developed.

答案3

得分: 0

这个回答中有一个很好的比较,清楚地解释了为什么Java同时使用AOT和JIT。

在这里提到的关于JIT的“微调”是在JVM的编译器中完成的,该编译器针对其运行的每个系统进行了优化。而且您不会遇到链接中列出的缺点。

英文:

There is a nice comparison in this answer which makes it clear to me why Java uses both, AOT and JIT.

The "fine-tuning" with JIT mentioned here is done in the compiler for the JVM, which is optimized for every system it runs on. And you don't take the disadvantages listed in the link.

答案4

得分: 0

JRockit JVM没有解释器。它会编译所有的字节码,即使在进行调试时也是如此,因此没有必要拥有解释器。

解释器的一个好处是更快的启动速度。例如,静态初始化程序只会执行一次,因此通常很少需要编译它。

英文:

The JRockit JVM doesn't have an interpreter. It compiles all the bytecode, even when doing debugging, so it is not necessary to have.

One benefit of an interpreter is faster startup. For example, a static initializer is only executed once, so there is usually little need to compile it.

答案5

得分: -2

解释器允许实时运行和调试代码。编译器必须预先编译才能运行,这意味着如果您的首选环境没有发现错误,那么您需要进行可能的修复并重新编译整个项目。

因此,它既有解释器又有编译器,以实现编译器的速度,同时允许开发人员通过进行小的更改来实时调试并检查修复,而无需每次重新编译整个项目。

简而言之,最大的原因在于:
编译器是终端用户运行的最快方式。
解释器对于开发者来说是调试和编码的最快方式。

英文:

An interpreter allows for on the fly running and debugging of code. A compiler must be pre-compiled to run which means that if there is an error that is not picked up by your preferred environment, then it would require you to make a possible fix and recompile the entire project.

Hence it has both an interpreter and a compiler to allow for the speed of a compiler and still allow devs to be able to debug on the fly by making small changes and check the fix without recompiling the entire project every time.

Essentially in a nutshell the biggest reason is this.
<br>A Compiler is the fastest way to run for an end user
<br>An Interpreter is the fastest way to debug and code for the developer

huangapple
  • 本文由 发表于 2020年9月23日 15:10:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/64022796.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定