Skip Headers
Oracle® JRockit Introduction
Release R28

Part Number E15058-05
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
PDF · Mobi · ePub

2 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 follows
Description of "Figure 2-1 The JRockit JVM as a Black Box"

Figure 2-2 shows what happens to Java code as it passes through the JRockit JVM. After Java code enters the JRockit JVM, it passes through the operations stage, the data structure stage, and the transformation stage, before emerging as binary code on a microchip.

Figure 2-2 Inside the JRockit JVM Black Box

Description of Figure 2-2 follows
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 follows
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;
   }
}

Originally, the code contained two calls to the 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.