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.
Wonderfully done!!!
ReplyDeleteInformative!
ReplyDeleteAwesome content and good write-up!
ReplyDeleteNicely written
ReplyDeletevery well classified
ReplyDeleteNice Content!!
ReplyDeleteAwesome Content.. 🙌🙌
ReplyDelete