Java : Interpreted or Compiled ? JIT and AOT !
Tags: java, programming, beginners
Published: 2023-08-27 08:00:18

As a programmer, you may have read about the concept of interpreted languages or compiled languages. 

Interpreted languages (example. BASIC) are those in which the source code is directly executed by the **interpreter**. Compiled languages (example. C++) on the other hand are those in which a **compiler** converts the source code into machine code before execution.

Is Java interpreted or compiled ? Traditionally speaking, the answer is that Java is both interpreted and compiled. But the more nuanced answer is that it depends on how you choose to execute your Java program.

The most popular (and maybe one could even say the "default") way of executing java programs is by using `javac` to covert the source code into **bytecode** and then executing the bytecode on a **Java Virtual Machine (JVM)**. 

While there are a number of JVMs to choose from, the most popular JVM on the planet is Oracle's HotSpot VM which was first introduced in 1999. If you have java installed on your machine, type `java --version` in the command line and inspect the output. You'll most likely see a reference to the HotSpot VM.

![HotSpot VM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c96y6q0a9s651qrutcn9.png)

Therefore, generally speaking what one would do is 

1. Use the Java Compiler (**javac**) which comes bundled in the Java Development Kit (**JDK**) to **compile** the source code (.java) and produce bytecode (.class) . 
2. Invoke the jvm using the **java** command so that the JVM can **interpret** the bytecode and execute it line by line. 
3. During interpretation, the JVM executes each line of bytecode by first converting it into the corresponding machine instruction. 
4. Corresponding to that line of bytecode, JVM submits the machine instruction to the CPU for the CPU to execute it.

But that's not all. Even during interpretation, the HotSpot VM analyses the "**hot**" (very simply put this means frequently invoked) areas of your program and caches the compiled version of those areas so that it does not have to repeat step 3 again and again. This is popularly known as **Just In Time Compilation** (JIT). So now you can probably see that the execution of a Java program is not simply "interpretation" or "compilation", rather it is a well balanced combination of both in the pursuit of portability and performance.

This is why, generally speaking, Java is considered to be a both interpreted and compiled language.

But, there are some other ways of executing Java programs which may be better suited in certain use cases.

One of these is the very popular [GraalVM](https://www.graalvm.org/) project which offers an **Ahead Of Time** (AOT) compilation capability using its [Native Image](https://www.graalvm.org/22.0/reference-manual/native-image/) technology to directly compile Java source code into native machine code for execution.

Let's see how the "**compilation**" and "**execution**" phases differ in both cases in terms of time performance. While there are a lot of other aspects to be compared, the time based comparison will give you a basic idea as to why AOT may be better than JIT for some use cases. 

We will use this program in both cases :

```
public class ComputeSquareRoots {
    public static void main(String[] args) {
        int numberOfRuns = 10000;
        long totalTime = 0;

        for (int i = 0; i < numberOfRuns; i++) {
            long startTime = System.nanoTime(); // Start time
            compute();
            long endTime = System.nanoTime(); // End time
            long executionTime = endTime - startTime;
            totalTime += executionTime;
        }

        double averageTime = (double) totalTime / numberOfRuns;

        System.out.println("Average Running Time: " + averageTime + " nanoseconds");
    }

    public static void compute() {
        for (int i = 0; i < 1_000_000; i++) {
            for (int j = 0; j < 100; j++) {
                Math.sqrt(i + j);
            }
        }
    }
}

```

Here is the command line output of each step.


![Command Line Output JIT vs AOT](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nm7mpqra882tk3n7ra5k.png)

As you can see

1. The compilation phase with `javac` took **~400ms** to produce the bytecode.
2. The execution phase with JVM took **~40,000ns** as reported by the output of our code.
3. The compilation phase for `native-image` was actually two step as it took the bytecode produced by javac as input. Therefore, total compilation time can be considered as **~400ms** + **~16800ms** which is **~17200ms**. **This is more than 40 times more as compared to JIT.**
4. The execution phase for the AOT compiled native executable was **~30ns** as reported by the output of our code. **This is more than 1000 times less as compared to JIT.**

So, while AOT has a longer compilation phase, it gives time advantage during the execution phase.

The example presented above is an over-simplified one to aid basic understanding. The choice between JIT and AOT in not as simple as this in most cases. There are a lot more aspects to consider. That being said, I hope this article helped you get a basic understanding of these concepts.