JIT Just-In-Time Compiler

 The Just-In-Time (JIT) compiler is a component of the Java Runtime Environment that enhances the performance of Java applications at run time.

Java programs consists of classes, which contain platform-neutral bytecodes that can be interpreted by a JVM on many different computer architectures. At run time, the JVM loads the class files, determines the semantics of every individual bytecode, and performs the required computation. The additional processor and memory requirement during interpretation means that a Java application runs more slowly than a native application. The JIT compiler helps enhance the performance of Java programs by compiling bytecodes into native machine code at run time.


Let’s understand this by taking 2 case examples 

§  Case 1:

§  In case 1, we can see that we are at the interpretation phase. Lets, assume we have 5 lines which are supposed to be interpreted to their corresponding machine code lines. So, as we can see in the Case 1, there is no JIT involved. Thus, the interpreter converts each line into its corresponding machine code line. However, if you notice the last 2 lines are the same. Clearly those lines are redundant and does not have any effect on the actual output but yet since the interpreter works line by line it still creates 5 lines of machine code for 5 lines of the bytecode.

§  Now this is inefficient right.

 

§  Case 2:

§  In case 2 we have the JIT compiler. Now before the bytecode is passed onto the interpreter for conversion to machine code, the JIT compiler scans the full code to see if it can be optimized. As it finds the last line is redundant it removes it from the bytecode and passes only 4 lines to the interpreter thus making it more efficient and faster as the interpreter now has 1 line less to interpret. So, this is how JIT compiler speeds up the overall execution process.

 

This was just one scenario where JIT compiler can help in making the execution process fast and efficient. There are other cases like inclusion of only those packages needed in the code, code optimizations, redundant code removal, etc which overall makes the process very fast and efficient. 


Bytecode Format (.class file)


§  Bytecodes are the Java virtual machine's machine language.

§  When a JVM loads a class file, it receives one stream of bytecodes for each method.

§  The bytecode streams are stored in the JVM's method area.

§  When a method is invoked while the programme is running, the bytecodes for that method are executed.

§  They can be executed through interpretation, just-in-time compilation, or any other technique chosen by the designer of a specific JVM.

§  Each instruction is made up of a one-byte opcode and one or more operands. The opcode specifies the action to be taken.




 

  •    Now let’s look at how a simple java program of a for loop would be converted in bytecode format.
  •    In the .class file along with our opcodes and operands, it contains some other useful information such as the size of the stack, number of local variables, and the argument size. Here stack = 2 means our stack if of the length 2 and it is 0-indexed i.e., it starts from 0, locals is the number of local variables in our program but looking at our java program we only see one variable i.e., the variable ‘i’. The other local variable is a java level virtual method. The args_size is the number of arguments.
  •  Now let’s look at the bytecode for this simple for loop program. iconst_0 pushes constant 0 onto the stack. Here the first character of our instruction generally indicates the type of datatype we are using, so in this case since there is an i at the start, that means we are pushing an integer in our stack. Now at the top of our stack we have constant 0. Next, we store the constant 0 in our locals slot. In other words, we pop constant 0 from stack and store it to locals slot at position 1. Then we load the value stored at locals position 1 and push it into our stack. In the third instruction ‘3:’ we push a byte sized integer 100 onto our stack. Thus, the command has ‘bi’ at the start to indicate the type of datatype we are pushing.  In the next instruction ‘5:’ we compare the two integers, and we branch to ‘14:’ if the integer at the top of the stack is smaller than the one below it or else it just moves through, in either case it empties the whole stack and then increments the locals slot 1 by 1. We jump to ‘2:’ and repeat the whole process again and again unless the integer at the top of the stack becomes smaller than the one below it.




Comments

Post a Comment