HotSpot

As you know, instead of compiling to native machine code, Java source is compiled to bytecodes in the form of class files. In the beginning, Java VMs were interpreters, deciphering one byte code at a time in a loop. Faster JIT (Just In Time) compilers appeared later, which compiled byte code to native code. A JIT compiler is basically a fast compiler that compiles at runtime. When a Java class is loaded, it is quickly compiled, even if methods in the class are used only once. Today you have HotSpot, a dynamic optimizing compiler. With HotSpot, methods are interpreted at first. When a method is called enough times—in other words, it's "hot"—HotSpot compiles and optimizes it. Also, HotSpot can use global analysis to better optimize, tuning those optimizations as the program runs, and to perform aggressive inlining. Inlining reduces the overhead of calling an often-used method. Two HotSpot VMs exist: client and server. The server has a few more optimizations than the client does. However, whereas the SDK includes both the client and server VMs, the JRE includes only the client VM. In addition to a few Java-specific optimizations, HotSpot implements many of the standard optimizations found in common C/C++ compilers, such as dead code elimination, loop invariant hoisting, common subexpression elimination, constant propagation, method inlining, and loop unrolling. We'll go over each of these optimizations so you'll know you don't have to perform these optimizations yourself.

Java-Specific Optimizations

These are some of the Java-specific optimizations HotSpot performs:

  • Fast synchronization. If two threads aren't trying to run the same method ("contention"), the method call is comparable in speed to a normal, unsynchronized method call. However, synchronized methods cannot be inlined.
  • Null check elimination. If HotSpot can prove that a field will never be null, it can remove the null check in the optimized code.
  • Range check elimination. Likewise, if HotSpot can prove that an array access will never be out or range, it can remove the range check in the optimized code.

In the next few sections, we show the performance optimizations that HotSpot performs on example code. Of course, HotSpot works with bytecode and machine code, not source code. But we'll show the equivalent source code version of what HotSpot is doing.

Dead Code Elimination

Dead code elimination is a technique to remove code that has no effect on the execution of the code. Take this function, for example:

public int function(int x) {
 int i = 100*x*x;
 return x;
}


Here, the i variable is set, but it's never used. So, HotSpot would eliminate the dead code:

public int function(int x) {
 return x;
}


Loop Invariant Hoisting

An invariant is something that doesn't change, such as a constant. Loop invariant hoisting (also called loop invariant code motion) is essentially moving invariants out of loops so they don't have to be recomputed over and over again. Take this loop, for example:

for (int i=0; i<array.length; i++) {
 int y = 4*x*x + 2*x;
 array[i] = y + i;
}


Here, the value y is the invariant because its value never changes while inside the loop. Its computation can be moved out of the loop:

int y = 4*x*x + 2*x;
for (int i=0; i<array.length; i++) {
 array[i] = y + i;
}


Common Subexpression Elimination

Subexpressions that are common (in other words, those that are seen more than once) can be eliminated by HotSpot. Consider this example:

int a = 4*x*x + 2*x + 3;
int b = 4*x*x + 2*x + 5;


You can precompute most of those two equations:

int temp = 4*x*x + 2*x;
int a = temp + 3;
int b = temp + 5;


Performing common subexpression elimination often makes code easier to read and can make code more maintainable if you have to modify the subexpression. So there's no harm in performing this optimization trick yourself.

Constant Propagation

A constant multiplied by a constant is still a constant. That's the basic idea with constant propagation, in which expressions involving constants (and no variables) are calculated only once, instead of over and over again. For example, this code:

int x = 10; // constant int a = 5*x; // therefore, also constant


would be compiled to:

int a = 50;


Loop Unrolling (Server VM Only)

Take a look at a typical loop:

for (int i=0; i<array.length; i++) {
 array[i] *= y;
}


The loop can be "unrolled" so that each statement is executed several times:

// (4 is a factor of array.length)
for (int i=0; i<array.length; i+=4) {
 array[i] *= y;
 array[i+1] *= y;
 array[i+2] *= y;
 array[i+3] *= y;
}


This speeds things up a bit by reducing the overhead of the loop itself: In the example, the check for the end of the loop and the counter increment occur four times less. However, this feature is in the HotSpot server VM only.

Method Inlining

One of HotSpot's strengths is its aggressive inlining capabilities. Inlining code reduces the overhead of calling a method. Take a glance at this example:

for (int i=0; i<array.length; i++) {
 array[i] = mathObject.calc(i);
}
...
public class MathClass {
 public void calc(int x) {
 return x * 5;
 }
}


The inlined code would look like this:

for (int i=0; i<array.length; i++) {
 array[i] = x * 5;
}


Aggressive method inlining is one of HotSpot's strengths. In languages such as C++, all methods are nonvirtual by default, meaning subclasses cannot override them. But in Java, the opposite is true: All methods are virtual by default, and methods are declared final to signify that they are nonvirtual. The good news is that HotSpot can analyze classes at runtime to determine whether a method is overridden by another class (thus, it is truly virtual). If not, the method can be inlined, even if it's not declared final. The example back in is a good example for tuning the code to make HotSpot perform inlining. In that example, you had an abstract Texture class and two subclasses, PowerOf2Texture and ShadedTexture. Unfortunately, when the reference was a Texture instead of one of its subclasses, HotSpot could not inline the virtual getColor() method. However, the getColor() methods of PowerOf2Texture and ShadedTexture were not virtual, so referencing these subclasses explicitly meant that HotSpot could perform inlining. In other words, instead of this:

Texture texture; // has virtual methods


you did this:

PowerOf2Texture texture; // has no virtual methods


HotSpot then could inline the getColor() method. HotSpot cannot inline a couple other types of methods as well. In short, HotSpot cannot inline methods that do the following:

  • Explicitly throw exceptions (unchecked methods, such as NullPointerException, are okay)
  • Are synchronized
  • Are overridden by another class (truly virtual)
Screenshot


   
Comments