Understanding Just-In-Time Compilation and Optimization
This chapter offers a high-level look at how the Oracle JRockit JVM generates code. It provides information about how the JRockit JVM compiles code just-in-time (JIT) and how it optimizes code to ensure high performance.
This chapter contains information about the following topics.
2.1 What Happens Inside the JRockit JVM
From the user's point of view, the JRockit JVM is a black box that converts Java code to highly optimized machine code for a specific platform (see Figure 2-1).
Figure 2-1 The JRockit JVM as a Black Box
Description of "Figure 2-1 The JRockit JVM as a Black Box"
Figure 2-2 Inside the JRockit JVM Black Box
Description of "Figure 2-2 Inside the JRockit JVM Black Box"
2.2 How the JRockit JVM Generates Machine Code for Java Applications
The code generator in the JRockit JVM runs in the background during the entire run time of your Java application, automatically adapting the code to run optimally. The code generator works in three steps, as depicted in Figure 2-3.
Figure 2-3 How the JRockit JVM Generates Machine Code for Your Java Application
Description of "Figure 2-3 How the JRockit JVM Generates Machine Code for Your Java Application"
Step 1: JIT Compilation
The first step in the machine-code generation process is just-in-time (JIT) compilation. JRockit does not include an interpreter; so the JIT compilation of the byte code into native machine code has to occur before a method executes. The JIT compilation is performed the first time a Java method is called.The JIT compiler is fast and generates moderately efficient code. This is necessary to enable the Java application to start and run quickly. Subsequently, profiling reveals frequently called methods (hot spots) that require further optimization. The JRockit approach—JIT compilation and no interpreter—results in relatively longer startup times, but even if the JIT compilation results in only moderately efficient code, the generated code is still significantly faster than interpreted code.
Step 2: Thread Monitoring
During the second step of the machine-code generation process, the JRockit JVM uses a sophisticated, low-cost, sampling based technique to identify which functions merit optimization: a "sampler thread" wakes up periodically and checks the status of several application threads. It identifies what each thread is executing and logs some execution history. This information is tracked for all the methods; when the information indicates that a method is heavily used (hot), that method is earmarked for optimization. Usually, a flurry of such optimization opportunities occur in the application's early run stages, with the rate slowing down as execution continues.
Step 3: Code Optimization
Code optimization is a process by which commonly-executed code is recompiled to make it run more efficiently.The first time that the JRockit JVM runs a method, the method is compiled into machine code. This compilation is quick, but the resulting code is not as efficient as it could be. This code is acceptable for methods that are run once and discarded; however, if a method is used repeatedly, the system can get a performance boost if the code for that particular method is regenerated in a more efficient way.
The JRockit JVM optimizes these commonly-executed (hot) methods to make the code as efficient as possible. The optimization runs in the background and does not interfere with the running application.
2.3 Examples of Code Optimization
The following code examples show how the JRockit JVM optimizes Java code.Example 2-1 shows the code before optimization.
Example 2-1 Code Before Optimization
class A { B b; public void newMethod() { y = b.get(); ...do stuff... z = b.get(); sum = y + z; } } class B { int value; final int get() { return value; } }Example 2-2 shows the optimized code.
Example 2-2 Code After Optimization
class A { B b; public void newMethod() { y = b.value; ...do stuff... sum = y + y; } } class B { int value; final int get() { return value; } }
b.get()
method. After optimization, the two method calls are optimized into a single variable-copy operation; that is, the optimized code does not need to perform a method call to acquire the field value of class B.
Optimization Process: Step-by-Step
Table 2-1 shows the tasks that the JRockit JVM performs to optimize the code in Example 2-1.Note that the optimization process usually does not occur at the Java source code level. Java source code is used here to provide an easily understandable view of the optimization process.
Table 2-1 Optimization Process: Step-by-Step
Optimization Step | Code Transformation | Comment |
---|---|---|
Starting point | public void newMethod() { y = b.get(); ...do stuff... z = b.get(); sum = y + z; } | |
Inline final method | public void newMethod() { y = b.value; ...do stuff... z = b.value; sum = y + z; } | // b.get() is replaced by b.value // latencies are reduced by accessing // b.value directly instead of using // a function call. |
Remove redundant loads | public void newMethod() {
y = b.value;
...do stuff...
z = y;
sum = y + z;
}
| // z = b.value is replaced with // z = y so that latencies are // reduced by accessing local value // instead of b.value. |
Copy propagation | public void newMethod() { y = b.value; ...do stuff... y = y; sum = y + y; } | // z = y is replaced by y = y because // there is no use for extra variable // z; the values of z and y are equal. |
Eliminate dead code | public void newMethod() { y = b.value; ...do stuff... sum = y + y; } | // y = y is unnecessary and can be // eliminated. |
0 Comments