Explanations regarding traditional interpreter, compiler and JIT compiler / interpreter

I am learning Java and the following things confuse me a little. I figured out the following:

  • Java Compiler -> Java Compiler simply converts programs .java

    to files .class

    , which means converting our source code to bytecode (this is a list of operating codes for a virtual machine (JVM), which makes Java platform independent).

  • Java interpreter -> just "interprets" the code and does not turn it into machine code. It executes each byte code instruction one by one as an instruction and executes it, no matter how many times the same instruction is executed. This is why it is slow and Java introduces the JIT concept.

  • JIT Compiler -> This also comes into play at runtime. The JIT compiler is able to improve performance by caching the results of blocks of code that have been translated, versus simply re-evaluating each line or operand in the bytecode every time it happens.

Now I have a few questions:

  1. Since my physical processor only understands native machine code, how is a Java program executed using the JVM interpreter? The interpreter does not convert bytecode to machine code. Until someone puts the machine code into memory, the physical processor cannot execute it.

  2. Suppose that somehow the interpreter also converts the bytecode to native machine code, then "cached execution unit (JIT) and line-by-line execution (interpreter)" is the only thing that distinguishes JIT and interpreter?

  3. If at runtime the JIT compiler converts the bytecode to machine code (for program execution), why doesn't Java use premature compilation? After generating JVM-dependent bytecode (which in turn makes Java platform independent), we can port it to the target machine where we want to execute it and just convert it to native machine code (by creating .exe

    or a .out

    file like in the case of C compilation). This may be possible because we have a specific JVM on each system. It would be much faster than using JIT compilation as it takes some time to compile and load the program It will still be platform independent by just distributing the bytecode (generated before the final translation from bytecode to machine code) ...

+4


source to share


2 answers


Disclaimer: take it all with a grain of salt; it's pretty simplistic.

1: You are correct that the computer itself does not understand the code, so the JVM itself is needed. Pretend XY

means "add the top two items to the stack and push the result." In this case, the JVM will be implemented something like this:

for(byte bytecode : codeToExecute) {
    if (bytecode == XX) {
        // ...do stuff...
    } else if (bytecode == XY) {
        int a = pop();
        int b = pop();
        push(a+b);
    } else if (bytecode == XZ) {
        // ...do stuff...
    } // ... and so on for each possible instruction ...
}

      



The JVM has implemented each individual instruction in its native code and essentially searches each piece of bytecode to find out how to execute it. By using JITting code, you can achieve significant speedup by skipping this interpretation (that is, see how each and every instruction should be processed). This is optimization.

2: JIT doesn't really execute code; everything still works inside the JVM. Essentially, JIT converts a chunk of bytecode into machine code when needed. When the JVM encounters this, it thinks: "Oh, this is already machine code! Honey, now I don't have to carefully check every byte, since the processor understands it itself! I just pump it up, and everything will magically work by itself!" ".

3: Yes, it is possible to pre-compile the code in such a way as to avoid early interpretation and JITting costs. However, in doing so, you are losing something very valuable. You see, when the JVM interprets the code, it also keeps statistics about everything. Then, when it JITs it, it knows how often different parts are used, which allows it to be optimized where it matters, speeding up the overall process at the expense of sparse elements, resulting in an overall increase in performance.

+4


source


  • After all, even the interpreter runs machine code equivalent to a bytecode instruction, it just happens indirectly. Think of it like a piano. The processor is like a gaming piano. The compiler punches holes into a strip of paper, which you then run on the piano. Using an interpreter essentially puts a humanoid machine in front of a piano that reads sheet music and pushes keys on the piano. In the end, the same chords sound, but there is an extra layer of indirection.

  • Yes. To a great extent.

  • As you say, Java was advertised as "compile once, run anywhere". Thus, bytecode is an essential function to fulfill this promise. The reason it doesn't compile when it arrives on your computer is practical: compilation is slow. This usually happens with software developers. If every Java application you run was compiled for the first time, you will be waiting for a while.

    Since most programs do not start completely (there are branches that are not accepted, menu items that you never select, etc.), it is usually faster to compile the parts you want because they are needed. It also saves disk space, as otherwise you will have two copies of each program on your computer.



However, you are not the first to think about it, and in fact some people write their programs in Java but use a compiler to create executables in them. Java compilers (-to-native) exist, they just aren't generally accepted because a lot of people are using portable C or C ++ at this point.

+1


source







All Articles