简体   繁体   中英

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

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?

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.

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.

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.

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).

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.
A Compiler is the fastest way to run for an end user
An Interpreter is the fastest way to debug and code for the developer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM