An explanation of the JVM compiler

Introduction of JIT

Interpreters and compilers

In some commercial virtual machines, Java programs are initially interpreted through the Interpreter, which identifies a method or block of code as “hot code” when the virtual machine detects that it is being run particularly frequently. To improve the efficiency of hot code execution, the Just In Time Compiler compiles the code to local platform-specific machine code at runtime and performs various levels of optimization.

During the compiler era, we were able to “compile once, run anywhere” by compiling source code into a. Class and masking the differences between the underlying computer operating system and hardware with the JVM’s cross-platform abstraction. At run time, the current mainstream JVMS are in -Xmixed mode, meaning interpreted runs and compiled runs are used together.

Starting with Java7, the HotSpot virtual machine defaults to a hierarchical compilation approach: the HotSpot methods are first compiled by the C1 compiler, and the hotspots within the HotSpot methods are further compiled by C2 (understood as secondary compilation, calculating better compilation optimizations based on previous runs). In order not to interfere with the normal running of the program, JIT compilation is carried out on an additional thread, and HotSpot allocates the number of C1 and C2 threads in a 1:2 ratio based on the actual CPU resources. , in the case of computer resources, the interpretation of the bytecode run and compile the runtime can simultaneously, compiled after the execution of the machine will start when the next call this method, has replaced the original explanation to execute (means have been translated into more efficient machine code, replace the original nature of the relatively low efficiency performance of methods).

2. Working mode

There is an old saying that “Java is interpreted execution.” Now it doesn’t look very accurate, and as to why, I’ll briefly explain execution: a line by line translation of compiled bytecode into machine code execution. Compilation execution: executed after a one-time translation of bytecode into machine code, in units of method.

3. Compare

  • Interpreter advantage: When a program needs to be started quickly, the interpreter can take effect first, saving compilation time and executing immediately. Explanation Execution takes up less memory space. At the same time, when aggressive optimizations by the compiler fail, they can be reversed to restore the interpreted execution.
  • Compiler advantage: When a program is running, the compiler comes into play over time. As more and more code is compiled into local code, more efficiency can be achieved.

4. Just-in-time compilation

Just-in-time compilation exists because it is one of the important means to improve program performance. According to the “80/20 rule” (that is, 20% of the code takes up 80% of the system resources), for most of the uncommon code, we do not need to waste time to compile it into machine code, but adopt the method of interpretation execution, and run it one by one. For some hot codes that only occupy a small part (important codes that can be considered to be executed repeatedly), it can be translated into efficient execution of machine code to improve the efficiency of the program, which is real-time compilation at runtime. HotSpot has two just-in-time compilers built into it, called Client Compiler and Server Compiler, or simply C1 Compiler and C2 Compiler.

  • C1: The Client compiler, which is oriented to Client GUI programs with requirements on startup performance, adopts relatively simple optimization methods, so the compilation time is short.
  • C2: The Server compiler, for the peak performance requirements of the Server program, the optimization method is complex, so the compilation time is long, but the performance is better during the run.

The current HotSpot compiler works by default with the interpreter and one of the just-in-time compilers, depending on the mode in which the virtual machine is running, which HotSpot will automatically choose based on its version and the hardware capabilities of the machine.

Users can also use the -client and -server parameters to force VMS to run in client mode (C1) or server mode (C2). In conjunction with what is known as “Mixed Mode”, Xint can be used to force the vm to run in “Interpreted Mode” where the compiler does not intervene at all.

In addition, -xcomp forces the virtual machine to run in “Compiled Mode”, where Compiled Mode takes precedence, but the interpreter still has to enter the execution if compilation cannot take place.

You can run the VM -version command to view the current default operating mode.

5. Identify as hotspot code

There are two types of “hot code” that are compiled on the fly, namely:

  • A method that is called multiple times

  • The body of a loop that is executed multiple times

    In the first case, the compiler treats the entire method as a compilation object, which is standard JIT compilation. The second one starts from the body of the loop, but the compiler still uses the entire method as the compilation object because it occurs during method execution, called on-stack substitution.

6.JIT trigger conditions

This behavior is called Hot Spot Detection to determine whether a piece of code is Hot code and whether it needs to be compiled immediately. There are two Detection algorithms:

  • Sample Based Hot Spot Detection (SAMple-based Hot Spot Detection) : The virtual machine periodically checks the top of each thread stack. If certain methods appear frequently on the top of the stack, this method is called a “Hot Spot method”. The benefits are simple, efficient implementation, and easy access to method call relationships. The disadvantage is that reduce, which is difficult to identify methods, is susceptible to thread blocking or other external disturbances.

  • Counter Based Hot Spot Detection: A Counter is established for each method (even a block of code), and if the number of executions exceeds a threshold, it is considered a “Hot Spot method”. The advantage is that the statistical results are accurate and rigorous. The disadvantage is that the implementation is cumbersome and the method invocation relationship cannot be directly obtained.

    HotSpot uses the second, Counter based HotSpot detection and has two types of counters: the method Invocation Counter and the Back Edge Counter. Both counters have a certain threshold that triggers JIT compilation.

    • Method call counter

      The default threshold is 1500 in Client mode and 10000 in Server mode. You can set this threshold by running -xx :CompileThreadhold. If nothing is done, a method call counter counts not the absolute number of times a method is called, but a relative frequency of execution, the number of times a method is called over a period of time. When a certain time limit is exceeded, the method’s call Counter is reduced by half if the method hasn’t been called enough times to commit to the just-in-time compiler, a process called Counter Decay. This period of Time becomes the Counter Half Life Time of statistics for this method. You can use the VM parameter -xx :CounterHalfLifeTime to set the time of the half-age period, in seconds.

    • Back edge counter

      A loopback counter is used to count the number of times the loop body code is executed in a method. The instruction that controls the redirection in the bytecode is called the “Back Edge”. Obviously, the purpose of setting up the back counter statistics is to trigger OSR compilation. For this counter threshold, HotSpot provides -xx: BackEdgeThreshold for the user to set, but the current VM actually uses -xx: OnStackReplacePercentage to briefly adjust the threshold as follows:

      • In Client mode, the formula is CompileThreshold X OSR ratio (OnStackReplacePercentage) / 100. Where the OSR ratio defaults to 933, then the threshold of the back counter is 13995.
      • In Server mode, Formula for method invocation counter threshold (the Compile Threashold) X (OSR (OnStackReplacePercentage) – interpreter monitoring ratio (InterpreterProfilePercent)) / 100 OnStackReplacePercentage the default value is 140, InterpreterProfilePercentage defaults to 33, if all the default values, then the Server mode virtual machine back to the side counter threshold for 10700.

C1 and C2 compilers

1. Explain the terms C1 and C2

Hotspot has two JIT just-in-time compilers built into it, the C1 and C2 compilers, which have different compilation processes.

C1 Compiler is a simple and fast Compiler, which mainly focuses on local optimization and is suitable for programs with short execution time or requirements on startup performance. It is also known as Client Compiler.

The C2 Compiler is a Compiler that performs performance tuning for long-running server-side applications. It is suitable for programs with long execution times or peak performance requirements, also known as Server Compiler.

Prior to Java1.7, the JIT was selected based on the program’s characteristics. By default, the virtual machine used the interpreter and one of the compilers, which meant that we had to choose between C1 for fast compilers and C2 for slow compilers. Fortunately, since Java1.7, layered compilation has been introduced, which combines the startup performance advantages of C1 with the optimized performance advantages of C2. Of course, we can also enforce just-in-time compilation mode for virtual machines with the -client or -server parameters. Hierarchical compilation divides the JVM’s execution state into five levels:

  • Layer 0: Program interprets execution. Profiling is enabled by default; if not enabled, layer 2 compilation is triggered
  • Layer 1: Bytecode is compiled into native code using the C1 compiler for simple, reliable optimization without turning on Profiling
  • Layer 2: Using the C1 compiler, Profiling is turned on and only C1 compilation is performed with number of method calls and number of loop back side executions Profiling
  • Layer 3: All C1 compilations with Profiling are performed using the C1 compiler
  • Layer 4: Using the C2 compiler, bytecode is also compiled into native code, but some optimizations that take a long time to compile are enabled, and some aggressive optimizations that are unreliable based on performance monitoring information are even made

For the three states of C1, the execution efficiency is from high to low: layer 1, Layer 2, and layer 3. C2 is typically over 30% more efficient than C1. In Java1.8, Hotspot has hierarchical compilation enabled by default. If you want to enable only C2, you can use the startup parameter -xx: -tieredcompilation to turn off hierarchical compilation. If you want to enable only C1, you can use the startup parameter. -xx :TieredStopAtLevel=1 Opens tiered compilation, specifying tier 1 compilation using the C1 compiler.

2.JVM parameter description

  • -xx: -tieredCompilation (close hierarchical compilation)

  • -xx :+TieredCompilation

    This parameter is used to enable hierarchical compilation of the JVM, which is enabled by default after JDK8. Parameter of TieredCompilation, which, if turned off, will cause the CodeCache to become smaller. This parameter should be used in conjunction with another codeCache parameter, otherwise it is easy to run out of codeCache, resulting in the efficiency of the program after a long time running

    In addition, hierarchical compilation is enabled by default in JDK8. The -client and -server configurations are invalid whether hierarchical compilation is enabled or disabled. If hierarchical compilation is turned off, the JVM will use C2 directly, which is configured -xx: -tieredCompilation; If you want to use C1 only, use the argument -xx :TieredStopAtLevel=1 while turning on hierarchical compilation

  • -xx :CompileThreshold=n(Specify how many times a method should be called so that HotSpot and JIT compilers can compile it)

  • -xcomp (specifies that the JVM compiles all bytecode into local code the first time it is used. The CompileThreshold = 1)

  • -xint (only in interpreted mode, no JIT compiler activated. CompileThreshold=0)

Three, practice

Use the HISDIS plug-in to view jit compiled and optimized assembly code

1. Environment construction

Decompress the downloaded plug-in to obtain two DLL files in the JDK_HOME/jre/bin/client and JDK_HOME/jre/binserver directories

2.Compilation generates assembly code

Once the plug-in is in place, you can compile using Java commands with parameters. The assembly code derived from bytecode is printed to the console.

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,com.lanyuan.controller.index.BackgroundController::testPrimeNumber -XX:CompileThreshold=10 
Copy the code

Parameter interpretation

  • – XX: + UnlockDiagnosticVMOptions (using the Product version of HotSpot. Need to add – XX: + UnlockDiagnosticVMOptions parameters, and this parameter must be in – XX: + PrintAssembly before)
  • -xx :+PrintAssembly(print jit optimized assembly code)
  • -xx :CompileCommand=compileonly, full class name :: function name (specify which function in which class to view)
  • -xx :CompileThreshold=10(a function is considered hot code after running 10 times, i.e. JIT is triggered)