The article directories

    • 1. An overview of the
    • 2. Compilation and interpretation
      • 2.1 Compiled Language
      • 2.2 Explain Language
    • 3. Write Once Run Anywhere
    • 4. Java compiler
    • 5. Java VM
      • 5.1 Architecture Overview
      • 5.2 Class loader
      • 5.3 Execution Engine
      • 5.4 Just-in-time Compiler (JIT)
    • 6. Performance comparison
      • 6.1 Fibonacci sequence performance test
      • 6.2 Performance test results
    • 7. Think about
    • Conclusion 8.

1. An overview of the

Programming languages are classified according to their level of abstraction. We distinguish between high-level languages (Java, Python, JavaScript, C ++, Go), low-level languages (assembler), and finally machine code.

Every high-level language code, such as Java, needs to be converted to machine-native code for execution. The translation process can be compilation or interpretation. But there is a third option. Try to use a combination of the two approaches.

2. Compilation and interpretation

Let’s start looking at some of the differences between compiled and interpreted languages.

2.1 Compiled Language

The compiler converts the compiled language (C ++, Go) directly into machine code.

They require explicit build steps before they can be executed. This is why you need to recompile the program every time you change the code.

Compiled languages are often faster and more efficient than interpreted languages. However, the machine code they generate is platform-specific.

2.2 Explain Language

In interpreted languages (Python, JavaScript), there are no build steps. Instead, the interpreter operates on the program’s source code as it executes the program.

Interpreted languages were once thought to be much slower than compiled languages. However, with the development of just-in-time compilation (JIT), the performance gap is closing. The JIT compiler converts code from interpreted language to machine code while the program is running.

In addition, we can execute interpreted language code on multiple platforms such as Windows, Linux, or Mac. Explain code is not associated with a particular type of CPU architecture.

3. Write Once Run Anywhere

Java and JVM are designed with portability in mind. As a result, Java code can be run on most of today’s popular platforms.

This may sound like a hint that Java is a purely interpreted language. Before execution, however, the Java source code needs to be compiled into bytecode. Bytecode is a special machine language inherent to the JVM. The JVM interprets and executes this code at runtime.

It is built and customized by the JVM for every platform that supports Java, not our program or library.

The JVM also has a JIT compiler. This means that the JVM optimizes our code at run time for performance benefits similar to those of the compiled language.

4. Java compiler

Javac’s command-line tools compile and convert Java source code into Java class files (xxx.class) platform-independent bytecode:

$ javac HelloWorld.java
Copy the code

Source files have a. Java suffix, while class files that contain bytecode have a. Class suffix.

5. Java VM

Compiled class files (bytecode) that can be executed by the JVM:

$ java HelloWorld
Hello Java!
Copy the code

How bytecode is converted to machine native code at run time.

5.1 Architecture Overview

The JVM consists of five parts:

  • Class loader
  • JVM memory structure
  • Execution engine
  • Local method interface
  • Local method library

5.2 Class loader

The JVM uses the ClassLoader to load the compiled class files into JVM memory

In addition to loading, the ClassLoader also performs linking and initialization.

  • Verify whether there are security holes in bytecode
  • Allocate memory for static variables
  • Replace the symbol memory reference with the original reference
  • Assign raw values to static variables
  • Execute all static code blocks

5.3 Execution Engine

The execution engine is responsible for reading bytecode, converting it to machine native code, and executing it.

Three main components are responsible for execution, including the interpreter and compiler:

  • Because the JVM is platform-independent, it uses an interpreter to execute bytecode
  • The JIT compiler compiles bytecode to native code at repeated method calls to improve performance.
  • The garbage collector collects and deletes all unreferenced objects.

The execution engine makes use of the Native method interface (JNI) to invoke local libraries and applications.

5.4 Just-in-time Compiler (JIT)

The main disadvantage of an interpreter is that every time a method is called, it needs to explain the execution, which is slower than compiled native code. Java uses a JIT compiler to overcome this problem.

JIT compilers cannot completely replace interpreters. The execution engine still uses it. However, the JVM uses the JIT compiler based on how often the method is called.

The JIT compiler compiles the bytecode of the entire method into machine-native code, so it can be reused directly. As with standard compilers, intermediate code is generated, optimized, and then machine-native code is generated.

Profiler is a special component of the JIT compiler that looks for hot spots. The JVM determines which code to compile based on the performance analysis information gathered at run time.

The effect is that, after several execution cycles, Java programs can perform their work more quickly. Once the JVM is aware of the hotspot, it can create native code to make it run faster.

6. Performance comparison

Let’s take a look at how JIT compilation can improve Java’s runtime performance.

6.1 Fibonacci sequence performance test

We will use a simple recursive method to calculate the NTH Fibonacci number:

private static int fibonacci(int index) {
    if (index <= 1) {
        return index;
    }
    return fibonacci(index-1) + fibonacci(index-2);
}
Copy the code

To measure the performance benefit of repeated method calls, we will run the Fibonacci method 100 times:

for (int i = 0; i < 100; i++) {
    long startTime = System.nanoTime();
    int result = fibonacci(12);
    long totalTime = System.nanoTime() - startTime;
    System.out.println(totalTime);
}
Copy the code

First, we compile and execute the Java code as normal:

$ java Fibonacci.java
Copy the code

We will then execute the same code with the JIT compiler disabled:

$ java -Djava.compiler=NONE Fibonacci.java
Copy the code

Finally, we’ll implement and run the same algorithms in C ++ and JavaScript for comparison.

6.2 Performance test results

Let’s take a look at the average performance measured in nanoseconds after running Fibonacci tests:

  • Java with a JIT compiler – 2726 ns – fastest
  • Java without a JIT compiler — 17965 ns — 559% slower
  • C ++ without O2 optimization — 9435 ns — reduced by 246%
  • C ++ with O2 optimization — 3639 ns — 33% slower
  • JavaScript — 22998 ns — 743% slow

In this example, Java performance improved by more than 500% with the JIT compiler. However, the JIT compiler does need to run some to run.

Interestingly, Even when C ++ is compiled with the O2 optimization flag enabled, Java performs 33% better than C ++ code. C ++ performed much better in the first few runs when Java was still being interpreted.

Java also trumps the equivalent JavaScript code that runs with Node, which also uses a JIT compiler. The results show a performance improvement of more than 700%. The main reason is that Java’s JIT compiler starts faster.

7. Think about

Technically, you can compile any static programming language code directly into machine code. You can also explain any programming code step by step.

Like many other modern programming languages, Java uses a combination of a compiler and an interpreter. The goal is to leverage the best of both worlds for high performance and platform-independent execution.

In this article, we focus on how HotSpot works. HotSpot is Oracle’s default open source JVM implementation. Graal VM is also based on HotSpot, so the same principles apply.

Today, the most popular JVM implementations use a combination of an interpreter and a JIT compiler. However, some of them may also use other methods.

Conclusion 8.

Java uses a combination of both approaches.

The source code we write in Java is first compiled into bytecode during the build process. The JVM then interprets the generated bytecode for execution. However, the JVM also uses a JIT compiler at run time to improve performance.

Translation:

  • www.baeldung.com/java-compil…

Keep warm together and make progress together

🍎QQ group [837324215] 🍎 pay attention to my public number [Java Factory interview officer], learn together 🍎🍎🍎