Abstract
This paper describes some guidelines for writing Java applications that use the IBM Java
Development Kit 6.0, based on analyzing the performance of various applications. These
guidelines are not strict rules, but serve to act as an aid for developers who need help in
obtaining good performance from their applications.
Introduction
The Java programming language is designed to facilitate software development by providing features
such as exception checks, dynamic class loading, interface and virtual dispatch and a framework for
automatic garbage collection. While these features originally imposed a significant performance overhead
challenge for Java virtual machines, over time, optimization techniques have been developed to help
reduce the cost of using these features significantly.
One of the major features of the IBM® J9 Java™ virtual machine (JVM) is an optimizing just-in-time (JIT)
compiler (called TRJIT) [21], which is designed to deliver high performance for different kinds of Java
applications. Developing Java software that is amenable to optimization by the JIT compiler is critical to
obtaining high performance.
Some of the features of the Java programming language that have just been outlined introduce inherent
uncertainties inadvertently. Uncertainties in application code make the optimizations of such code difficult
and this, in turn, makes the application run slower. However, complicating the Java code for the sake of
the JIT compiler is something to generally avoid; you should only do so when absolutely necessary.
Optimizing Java code by making the code simpler is usually a good idea, but there are many reasons not
to complicate code to make it faster. Firstly, complicated code usually has more bugs and is harder to
maintain. Secondly, optimizing Java code for the current virtual machine, JIT technology and execution
environment (which includes the hardware and operating system) might make things worse for future
technology and run environments.
This paper outlines some guidelines for writing high-performance Java code that is amenable for
optimization by the TRJIT compiler. You should apply these guidelines to the important parts of your
application code (to the hotspots), as determined by using profiling tools, to obtain the best performance.
This paper is organized as follows: The first section contains a background of the JIT technology. It also
provides a brief overview of the challenges faced and the approaches taken by the TRJIT compiler to
optimize Java code. The next section outlines some suggestions for writing Java applications from the
perspective of the TRJIT compiler. Finally, this paper briefly touches upon tuning application code.
IBM Just-In-Time Compiler (JIT) for Java
Best practices and coding guidelines for improving performance
JIT technology
This section sketches some of the challenges of optimizing Java applications and strategies that the
TRJIT compiler employs to optimize such applications.
Compilation happens during run time
Unlike static applications, a JIT compiler dynamically compiles Java methods during the running of the
application. Although dynamic compilation carries a runtime overhead, it also allows the JIT compiler to
collect runtime information and optimize the application for the specific target platform.
Strategy: Focus optimization effort on important code
Given the runtime overhead, the JIT focuses its optimization efforts on the most frequently invoked
parts of the application to maximize the application’s performance. To determine which methods are
important, a sampling thread periodically checks which method the virtual machine is currently
running on each Java thread. The TRJIT compiler chooses a method for compilation only if it is
invoked frequently, or if the sampling thread indicates that the method has been running for a long
time.
The TRJIT compiler employs an adaptive-compilation strategy. The first compilation of a method is
usually done at a low optimization level, resulting in a
cheap compile (in terms of time and
computational resources). The newly compiled code runs when the method is next invoked. Some
methods that are long-running and that are only invoked one time (for example, the main() method)
are optimized by transitioning them from interpreted to compiled code through on-stack replacement
[7]. A recompilation of a method is triggered if the sampling thread indicates that the method runs
frequently. This strategy guarantees that the JIT compiler focuses on the most important parts of the
application code. Recompilations are usually performed at higher optimization levels. Methods that
run for more than roughly 10 percent of the total run time compile at the highest optimization level,
though this is not guaranteed.
The TRJIT compiler employs aggressive-feedback-directed profiling to assist compilations that are
performed at higher optimization levels [1]. When a method is chosen for compilation at higher
optimization levels, a temporary instrumented compilation is first done. The instrumented method
runs for a short duration to collect profiling information, which is in turn used to optimize the method.
The virtual machine also performs profiling during bytecode interpretation; the JIT compiler then uses
this information (in addition to the profiling information described earlier) to optimize the method.
IBM Just-In-Time Compiler (JIT) for Java
Best practices and coding guidelines for improving performance