3.2.1. Java Compiler Optimizations
Due to the unique features of the JVM, such as the stack architecture, dynamic loading, and exception handling, Java native code compilers need to apply different types of optimizations than those used in traditional compilers. Some commonly implemented optimizations include mapping stack variables to machine registers, moving constant stack variables into native instructions, increasing the efficiency of exception handling, and in lining of methods. Mapping variables to machine registers increases native code performance since registers are much faster to access than memory. Registers can also be used to reduce the amount of stack duplication in Java. Specifically, each Java bytecode instruction consumes stack variables. Therefore, if a bytecode operand is used by multiple bytecode instructions, it must be duplicated on the stack. Register operands can be used over multiple instructions, however, eliminating the stack duplication overhead for those instructions.
Stack activity also can be reduced by moving constants on the stack into native instructions as immediate operands. Java bytecode instructions must receive constant operands from the stack. Since most architectures support instructions with immediate operands, this optimization eliminates the overhead of loading the constant into a register or memory location.
Exception overhead can be reduced by eliminating unnecessary exception checks and increasing the efficiency of the exception checking mechanism. For example,arrays are often used in iterative loops.If an array index value remains bounded inside of a loop, array bounds checks can be eliminated inside of the loop, which can produce significant performance advantages for large arrays.
Inlining of static methods is useful for speeding up the execution of method calls. However, the Java to bytecode compiler can only inline static methods within the class because static methods in other classes may be changed before actual execution. The implementation of any single class instantiation is stable at runtime and can therefore be inlined. The JIT compilers described in Section 3.2.2 can make use of this fact to inline small static methods to thereby reduce the overall number of static method calls. Dynamic methods can be converted to static methods given a set of classes using class hierarchy analysis [Dean et al. 1995]. If a virtual method has not been overloaded in the class hierarchy, dynamic calls to that method can be replaced by static calls. However, class hierarchy analysis may be invalidated if a new class is dynamically loaded into the program.