A bridge between programs and machines
1. Small talk
I believe that many friends will encounter language barriers when traveling abroad or communicating with foreign friends. At this time, we need to master the corresponding foreign language or have a translator. The author can only speak Chinese, so I need a translator to communicate with foreign friends who do not understand Chinese. Our execution engine is similar to this "translator".
2. Overview
The role of the execution engine is to interpret or compile bytecode instructions into native machine instructions on the corresponding platform. In simple terms, the execution engine acts as a translator from high-level language to machine language. For the Hotspot virtual machine, there are two parts in the execution engine: the interpreter and the JIT compiler (just-in-time compiler). The following figure is the principle of the execution engine:
3. Interpreter
The role of the interpreter is a runtime 翻译者
, which converts the content of the bytecode file 翻译
to the local machine code instructions of the corresponding platform. After a bytecode instruction is interpreted and executed, the interpretation operation is then performed according to the next bytecode instruction to be executed recorded in the pc register. There are two sets of JVM interpreters, one is ancient 字节码解释器
, and the other is commonly used now 模板解释器
.
1. Bytecode Interpreter
The bytecode interpreter simulates bytecode execution through 纯软件代码
during execution, which is very inefficient.
2. Template interpreter
The template interpreter will 每一条字节码和一个模板函数关联
, the template function will directly generate the machine code when this bytecode instruction is executed, thus improving the performance of the interpreter. In the commonly used HotSpot VM, the interpreter is mainly composed of Interpreter templates and code modules. Interpreter template: Implements the core functions of the interpreter. code module: used to manage the native machine code instructions generated by the HotSpot VM at runtime.
4. Just-in-time compiler (JIT compiler)
The purpose of the just-in-time compiler is to prevent the function from being interpreted and executed, but to compile the entire function body into machine code instructions. Each time the function is executed, only the compiled machine code can be executed. This method can greatly improve the efficiency.
1. Hotspot codes and detection methods
Of course, whether the JIT compiler is required to directly compile the bytecode into the machine code of the corresponding platform depends on the code being called 执行频率
. The bytecode that needs to be compiled into machine code by the JIT compiler, also known as 热点代码
, the JIT compiler will make 深度优化
for the hot code, and compile it from bytecode into The machine code, 并缓存到方法区
, improves the execution efficiency of the code.
The way of JIT compilation occurs during the method execution process, so it is also called _on-stack replacement_, or OSR (On Stack Replacement) compilation for short. Through the method of 热点探测
, judge how many times a method is called, or how many times the loop body is executed before reaching the threshold, and then compile. The method of Hotspot VM hotspot detection is based on counters. This technology-based hotspot detection method is divided into two types: 1. Method call counter 2. Back edge counter
Regarding the replacement on the stack, the author will not go into details here. Interested partners can learn about it by themselves.
1.1 Method call counter
The method call counter is used to count the number of method calls. Its default threshold is 1500 times in client mode and 10000 times in server mode. Exceeding this threshold triggers JIT compilation. Of course, this threshold can also be manually specified by modifying the virtual machine parameters -XX:CompileThreshold
.
When a method is called, it will firstly check whether the method has been JIT compiled. If it exists, the compiled native code will be used to execute it. If it does not exist, the call counter of this method will be incremented by one, and then judged. Whether the value of the counter exceeds the configured threshold. If it has been exceeded, a compilation request for this method will be submitted to the JIT compiler. Below is the flow chart of the method call counter execution:
Regarding the method call counter, if no setting is made, the method call counter counts not the absolute number of times the method is called, but a relative execution frequency. When a certain time limit is exceeded, if the number of method calls still does not reach the threshold, the call counter of this method will be reduced by half. This process is called the method call counter 热度衰减
, and this time 半衰周期
is called the method.
The process of thermal decay is carried out by the way when the virtual machine is garbage collected, and it is just a small effort. Thermal decay can be turned off using the virtual machine parameter -XX:-UseCounterDecay
. This way, most methods will be compiled to native code as long as the runtime is long enough. Finally, you can also use the -XX:CounterHalfLifeTime
parameter to set the time of the half-life cycle in seconds.
1.2 Back edge counter
Its function is to count a method 循环体代码执行次数
, after encountering the control flow direction in the bytecode, the jump instruction is called "back edge". Obviously, the purpose of establishing back edge counter statistics is to trigger OSR compilation. The following is a flow chart of the execution of the back edge counter:
About OSR compilation mentioned above
2. Just-in-time compiler classification
In Hotspot VM, there are two JIT compilers built-in, namely client compiler and server compiler, but in most cases we refer to C1 compiler and C2 compiler for short. You can specify which JIT compiler the JVM uses at runtime through the command display.
2.1 c1 compiler
Specifies that the Java virtual machine runs in client mode and uses the C1 compiler. The C1 compiler performs simple and reliable optimizations on the bytecode in a short time. In order to achieve faster compilation speed, but the compiled code execution speed is relatively slow. The C1 compiler mainly has method inlining, de-virtualization, and redundancy elimination.
- Method inlining: Compile the referenced function code to the reference point, which can reduce the generation of stack frames, parameter passing and jumping process.
- Devirtualization: Inlining the only implemented class.
- Redundancy elimination: stacking some code that will not execute during runtime.
2.2 c2 compiler
Specifies that the Java virtual machine runs in server mode and uses the C2 compiler. The C2 compiler takes a long time to optimize the code, and the compilation time is also long. But the compiled code executes faster. The optimization of C2 is mainly at the global level, the basis of escape analysis optimization. Based on escape analysis, there are several optimizations on C2 as follows:
- Scalar Substitution: Substitute a scalar value for a property value of an aggregate object.
- On-stack allocation: For unescaped objects allocated on the stack instead of the heap.
- Synchronization elimination: clear synchronization operation, usually referred to as synchronized.
2.3 Graal Compiler
Since JDK10, after the C1 compiler and the C2 compiler, a Graal just-in-time compiler has been added to the HotSpot VM. The compilation effect is equal to the C2 compiler in just a few years. At present, with the "Experimental Status" label, you need to use the switch parameters -XX:+UnlockExperimentalVMOptions
, -XX:+UseJVMCICompiler
to activate the compiler before it can be used.
5. Interpreter and JIT coexist
There are several reasons why the coexistence of the interpreter and the JIT is required:
- When the program starts, the interpreter can be used immediately, saving the time of compilation.
- If the compiler wants to execute, it needs to compile the bytecode into local machine code, and cache the compiled machine code, which takes a certain amount of time to compile.
- The compiled local machine code has high execution efficiency. Therefore, in the two coexisting modes, the interpreter plays a role first, and does not have to wait until the real-time compiler is fully compiled and executed, which can save unnecessary compilation time.
- As the program continues to run, the compiler comes into play. According to the
热点探测
function, more and more bytecodes are compiled into local machine code to obtain higher execution efficiency.
6. The way the execution engine executes the program
By default, HotSpot VM adopts an architecture in which the interpreter and the JIT compiler coexist. Of course, readers can specify whether the virtual machine should be executed entirely by the interpreter at runtime through virtual machine parameters according to specific application scenarios. It is still completely executed with a just-in-time compiler.
-
-Xint
: execute the program completely in interpreter mode -
-XComp
: Execute the program completely in just-in-time compiler mode. If there is a problem with the just-in-time compiler, the interpreter will step in; -
-Xmixed
: The mixed mode of interpreter + real-time compiler is used to jointly execute the program. HotStop VM defaults to this mode.
Seven, reference source code
编程文档:
https://gitee.com/cicadasmile/butte-java-note
应用仓库:
https://gitee.com/cicadasmile/butte-flyer-parent
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。