JIT Java | Just In Time (JIT) Compiler

Vidushika Dasanayaka
Nerd For Tech
Published in
7 min readSep 20, 2021

Good day, readers! I will discuss the Java JIT compiler today in this article. Actually, what is this JIT compiler? Is it the same as the normal compilation we already know? If you are curious please make sure to read this article.

Before we get into the JIT compiler, it’s vital to understand how a program runs on our computer. There are two types which we can classify how a program runs on our computer.

  1. Execute the program directly on our operating system.
  2. Execute the program on a virtual machine that is run on our Operating system

Let’s talk a bit about the execution of the program directly on OS.

So, let’s say you’re writing a C program. So after your writing, you have the C code or source code. Next, you will run that code using cmd or your IDE. In this case, the C compiler which is software that runs on OS will convert this source code into a .exe file.

A compiler is a software program that translates computer code written in one programming language into another language. A compiler is used for programs that translate source code from a high-level programming language to a lower-level language.

So at last we have an executable file that can directly run our OS. This executable file is platform dependant. In other words, If you run your C compiler on a Linux system, it will generate executable files that are Linux compatible. Your C compiler will generate a Windows-compatible executable file if you execute it on a Windows machine. (So, we can name this as native code). So after the execution of the program, we can get the executable file that is directly run by our OS.

How does a program run on a Virtual Machine?

We will continue this by using Java Virtual Machine(JVM).

This is the full execution of the Java program. As you can see unlike the C program, the Java program is both compiled and interpreted. So first, look into the compilation of java code.

There are 3 steps in compilation time.

  1. Writing the Java code(abc.java as in diagram)
  2. Compilation
  3. Getting the byte code after compilation (abc. class file as in diagram)

In this process, we will first write our java code and save it as a .java file (abc.java). So with command javac abc.java , next, we will invoke the javac compiler which converts java code into byte code. Here we can get the .class file (abc.class) after compilation. Actually, this bytecode cannot that understand by our OS directly. This only can understand by the Java virtual machine on our RAM.

According to this analogy, a virtual machine is a program that allows you to run a program (byte code) on this machine rather than directly on the operating system. When we run our code directly on our operating system, we refer to it as native code, and when we run it in a virtual machine, we refer to it as byte code.

This is the end of compile-time in java. Now next we will move to the run time of the java program.

As of the end of the compile-time, we get the bytecode which is a .class file. So, next with the java abc command we will load .class files/bytecodes into the class loader which is inside the JVM.

Get some idea about JVM(java virtual machine)

Then next, the bytecode verifier inside JVM verify that the bytecode and ensures that it will not violate security requirements. Right after that, invoke of the interpreter or JIT(Just In Time) compiler happens. (We will talk about the JIT compiler more precisely after this section.) When we talk about the interpreter, it converts the bytecode into machine code(native code)by reading line by line.

The main difference between compiler and interpreter is, the compiler compiles the whole code at once into machine code(native code). While the interpreter converts code into machine code by reading line by line. So we can say that the Java program is slower than other languages like C because it uses interpreters during run time.

After the interpreter interprets the code then it transfers to the OS and is executed. So this is how the program runs in a virtual machine without directly running on an OS.

Now let’s move to our main topic,

The Just in Time(JIT) compiler

The JIT compiler converts recurring bytecode code blocks into machine code, which the interpreter can use immediately. In Java, JIT is an essential component of the JVM. It improves execution performance by a factor of ten compared to the preceding level. To put it another way, it’s a long-running, computer-intensive application that optimizes performance. It improves the performance of a Java application during compilation or execution.

The JIT compiler is located inside the JVM. Let’s see how the JVM is organized.

JVM Architecture

The java virtual machine is consist of 3 components

  1. Classloader
  2. Runtime memory
  3. Execution Engine

JIT compiler is located inside the execution engine. From the JVM, the Execution Engine has a few sub-components. For the learning of the JIT compiler, we need only the following components.

  1. Interpreter
  2. JIT compiler
  3. Profiler

As I mentioned earlier, the JIT compiler identifies recurring program blocks and converts them into machine code. So interpreter will not convert those recurring bytecodes one by one. JIT compiler will supply machine codes of recurring bytecodes to the interpreter for execution. Likewise, JIT Compiler optimize the performance of Java application. So how does the JIT compiler identifies the recurring code blocks?

For that, let’s see the organization of the Execution Engine.

As you can see in the diagram profiler is the component that is responsible for identifying recurring code blocks. Let's see an example.

public class Main {
static void myMethod() {
System.out.println("Hello World!");
}
public static void main(String[] args) { do{
myMethod();
}while(true);
}
}

After compilation of this java code by the javac compiler, we get the bytecode. The Bytecode of the above code is executed by the interpreter line by line. So we can see that the myMethod() is calling multiple times. In this case, the profiler will identify this as a recurring code block from the whole program. The profiler in the execution machine maintains a counter which counts the number of calls that occur for a method. When this count passes the threshold value predefined in JVM, the JIT compiler compiles it into a native code so that the interpreter uses that native code in the next myMethod() call. JIT compiler will not stop at this moment if that particular code block passes the next threshold value, JIT compiler again optimized the code block. This process is happen in 4 stages. The JIT compiler has two compilers for this purpose. The C1 and C2 compilers are two different compilers. For the C1 compiler, we call it the client compiler, and for the C2 compiler, we call it the server compiler.

Compilations and optimizations at levels 1, 2, and 3 are handled by the C1 compiler. Level 4 compilation and optimization are handled by the C2 compiler.

This isn’t the end, though. JVM makes even more efforts to reduce the amount of time it takes to complete a task. A memory section known as the code cache exists. It’s similar to our computer’s cache memory. It has limited memory, and JVM flags can be used to modify the code cache. When a code block is called multiple times, the JIT compiler places that method in the code cache so that the interpreter may quickly access that code block. However, because it is a tradeoff, the JIT compiler does not add all of the codes to this code cache. The JIT compiler is clever in this regard, as it can achieve maximum efficiency.

So this is how the JIT compiler optimizes the java code in simple terms. There are some advantages as well as disadvantages in JIT compilation.

Advantages:

  • JIT compilers require less memory.
  • After a program has started, JIT compilers run.
  • While the code is running, it is possible to optimize it.
  • Any page errors can be minimized.
  • On the same page, code that is used together will be localized.
  • Different levels of optimization can be used.

Disadvantages:

  • It can take a long time for your computer to boot up.
  • Cache memory is heavily used.
  • In a Java application, this increases the level of complexity.

So this is the end of my article. This is a small description of the JIT compiler as I learned up to now. I hope you found something useful in my article. Thank you very much 💖.

--

--

Vidushika Dasanayaka
Nerd For Tech

Undergraduate of Faculty of Information Technology, University Of Moratuwa