0

I fail to understand the difference between an interpreter and JIT. For instance, from this answer:

JVM is Java Virtual Machine -- Runs/ Interprets/ translates Bytecode into Native Machine Code

JIT is Just In Time Compiler -- Compiles the given bytecode instruction sequence to machine code at runtime before executing it natively. It's main purpose is to do heavy optimizations in performance.

Both produce native machine code. Then, from this other answer:

An interpreter generates and executes machine code instructions on the fly for each instruction, regardless of whether it has previously been executed. A JIT caches the instructions that have been previously interpreted to machine code, and reuses those native machine code instructions.

As I see it, an interpreter is similar to a JIT in that it also translates the bytecode into native code, and the difference is that JIT performs some optimization, like caching.

Is this right? Is there any other major difference?

antonro
  • 449
  • 2
  • 4
  • 10
  • The answer is nonsense, as I stated in a comment a year ago, with three upticks. Self-evident nonsense, too, as there is no apparent distinction between the two definitions given. – user207421 May 08 '18 at 11:23

2 Answers2

3

I think the above definitions aren't necessarily true.

It isn't "mandatory" or "necessary" that an interpreter translates into machine code.

In essence, an interpreter interprets. It finds a loop, and then "runs" that loop. That is not the same as creating machine code that executes a loop.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • "It finds a loop, and then 'runs' that loop". Sorry but I'm lost here. How does it run it without creating machine code? The machine only understands machine code. – antonro May 08 '18 at 11:21
  • @Michael It's stated there "Upon every successive execution, the JVM merely uses the already compiled native code to optimize performance". That is, it caches the compiled code, what I said in the question. – antonro May 08 '18 at 11:23
  • 1
    @antonro an interpreter in the narrow sense doesn’t generate any native code, because it does already contain pre-built code for every bytecode instruction. Think of having ~200 subroutines, one for each instruction. Then, it has to read the current opcode, execute the right subroutine and advance to the next opcode. That’s the bare minimum. In practice, it may contain additional special routines to handle common patterns or invocations of certain well-known methods. Some implementations generate the lookup table at startup-time, but that’s still different to translating the bytecode. – Holger May 08 '18 at 13:22
  • @Holger Thanks, that's really the information that I was missing, very helpful. If you put it in an answer I'll accept it. – antonro May 08 '18 at 16:55
2

This statement:

An interpreter generates and executes machine code instructions

Is false.

Simply put, an interpreter is a program that loops over the instructions of a program (be they from a virtual or real instruction set), and executes them one by one. This is done by programming out what each instruction should do and simulating that within the interpreter.

On the most simple level, you could imagine an interpreter looks something like this in general:

for(byte byteCode : program) {
    if(byteCode == ADD_BYTECODE) {
        add();
    }
    // ... others
}

This is not so different a concept from how a CPU executes machine code, but in the case of a CPU, most of the logic is implemented in hardware directly.

I suppose you could say that an interpreter is a program that simulates a CPU in software.


The JIT compiler does the job of translating byte code into machine code and optimizing it along the way too. One of the theoretical advantages of machine code over byte code is for instance that a particular CPU might have specialized instructions available that run faster than the byte code equivalent.

In the case of the JVM this is done when a method is "hot", i.e. when it is ran a lot. JIT compilation takes a long time however (try running a Java program with the -XX:-TieredCompilation -Xcomp flags, which force C2 compilation by default, you'll see the difference in startup time), so it's faster to interpret the byte code first. That also gives the opportunity to collect profiling data, which is data about how the program is running (e.g. how many times an if branch is triggered, or which types are used for dynamic dispatch calls). The profiling data is also used during JIT compilation to do better optimization.

Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93