HOME  |   TRAINING  |   FREE TUTORIALS
Find out more about our new RSS feed.
FREE Tutorial
JAVA: PERFORMANCE TUNING AND MEMORY MANAGEMENT PART 3 - COMPILERS

CATEGORY
SEARCH OUR OTHER TUTORIALS

DESCRIPTION

When you compile a Java source code file, the compiler generates a class file containing Java bytecodes that can be processed by a Java Virtual Machine.


This free tutorial is a sample from the book Professional Java Programming.


Other languages are usually compiled into machine code specific to one type of processor, but Java's bytecodes are not tied to a particular operating system or processor type, allowing Java programs to run on different platforms without modification.

Interpretation vs. Compilation

Most languages are compiled, meaning that their source code is converted into equivalent machine code that will run on some processor or processor family. In contrast, Java is interpreted by nature, which means that compiling it generates bytecodes that must be processed by some application, in this case a Java Virtual Machine.

While interpretation does allow Java to run on different platforms by implementing a JVM for each one, it also causes Java code to run more slowly than a compiled application would. Although compilation is itself a relatively slow and CPU-intensive operation, it can be done by the developer before the software is ever installed on users' machines. In contrast, Java code is compiled into platform-neutral bytecodes to make it useable on more than one platform, and that bytecode is then interpreted when it's finally executed. Some native compilers for Java are now available.

Just-In-Time Compilers

Once Java code is being executed on a specific platform, there's no reason that the bytecodes can't be temporarily converted into machine code so that it will run more quickly. In fact, that's exactly what a Just-In-Time (JIT) compiler does: creates a machine code representation of the Java bytecodes at execution time. Before a JIT-enabled JVM executes a method, it creates a machine representation of the code in that method in memory, and any calls to that method will be handled by the faster machine representation instead of by an interpreter.

Since compilation is slow, this approach may initially cause the code to run more slowly than an interpreter would, but only when the code is executed for the first time. However, a typical application executes some portion(s) of its code repeatedly, and when that is the case, a JIT compiler will usually cause the application to run much more quickly than it would when executed by a purely interpretive JVM.

Most current JVM implementations include a JIT compiler, which is also the reason that you'll often see ("Compiled Code") in stack traces like the one shown below:

Exception in thread "main" java.lang.NullPointerException
at Test.doStuff(Test.java, Compiled Code)
at Test.main(Test.java, Compiled Code)

This occurs because it's no longer possible to determine the line number associated with a particular method invocation once the JIT compiler has processed the code. However, some JVM implementations allow you to disable the JIT, which is usually done by specifying an option such as -nojit when executing the JVM or by setting the java.compiler system property to NONE. For example, depending on which technique your JVM supports you could use one of the following commands to execute an application named MyTest with the JIT disabled:

java -nojit MyTest

or:

java -Djava.compiler=NONE MyTest

Once the compiler is disabled, stack trace entries will normally contain the line number along with the class, method, and file name as shown below:

Exception in thread "main" java.lang.NullPointerException
at Test.doStuff(Test.java:10)
at Test.main(Test.java:5)

HotSpot Technology

Even with the improved performance provided by JIT-enabled JVM implementations, Java is sometimes perceived as executing too slowly, but JavaSoft's HotSpot technology provides an even greater boost to Java's speed.

Like a JIT compiler, HotSpot technology is integrated into a JVM and it performs a similar function, but it does so in a much more sophisticated way. HotSpot includes architectural improvements over previous JVM implementations, including faster and more efficient object handling, garbage collection, and thread synchronization.

For example, objects can be accessed more quickly and they occupy less memory. The garbage collector also does a better job of reclaiming unused objects and it does so without any noticeable impact upon your application's performance. In addition, monitor/synchronization processing is much faster, so there is less of a performance penalty associated with making methods and blocks of code synchronized. Lastly, HotSpot also performs runtime method inlining for you, which prevents you from having to make changes to your code that may make it less readable and more difficult to maintain.

Central to HotSpot technology is the HotSpot compiler that provides adaptive optimization. While a JIT compiler converts bytecodes into native code indiscriminately, the HotSpot compiler identifies hot spots within your application by "observing" its execution. The compiler then generates native code only for selected areas that are executed repeatedly, or that would benefit from compilation for some other reason. By being more selective in which portions of your code it compiles, the HotSpot compiler is able to spend less time compiling code and/or make the compiled code extremely efficient.

As of this writing, there are two different implementations of HotSpot: one that's targeted for client-side code, and another that's intended for server-side applications. The client version is called the Java HotSpot Client VM, and is integrated into version 1.3 of the Java SDK. The server edition, called the Java HotSpot Server VM, is currently available only as an early access download through the Java Developer Connection, but should be provided as a full-fledged release in the near future.

An obvious question is how much of an improvement can you expect to see by using HotSpot. The answer depends very heavily upon the nature of your application, and if your program spends most of its time waiting for user input, the answer is that you'll see little or no improvement with HotSpot. However, a CPU-intensive application could easily execute twice or even three times as quickly under a HotSpot JVM as it would with a "classic" JVM implementation.

Disassembling Code

The javap utility provided with the Java Software Development Kit can be used to disassemble a class file, which simply converts the Java bytecodes into equivalent Java source code. For example, suppose that you create and compile the following application that adds two numbers together and prints the result:

public class AddNumbers {

public static void main(String[] args) throws Exception {
 int a = Integer.parseInt(args[0]);
 int b = Integer.parseInt(args[1]);
 System.out.println("Sum = " + (a + b));
}

}

You can use javap to view the bytecodes that were generated when the file was compiled by entering the following command:

javap -c AddNumbers

When the command executes, it produces output like that shown below, with the numbered lines after the two Method statements representing Java bytecode instructions. The first method is the default constructor for AddNumbers, which was generated by the compiler since no constructor was defined explicitly. The other Method represents the main() method, and the bytecodes below it will be executed when this application is run:

Compiled from AddNumbers.java
public class AddNumbers extends java.lang.Object {
public AddNumbers();
public static void main(java.lang.String[]) throws java.lang.Exception;
}

Method AddNumbers()
0 aload_0
1 invokespecial #9 <Method java.lang.Object()>
4 return

Method void main(java.lang.String[])
0 aload_0
1 iconst_0
2 aaload
3 invokestatic #13 <Method int parseInt(java.lang.String)>
6 istore_1
7 aload_0
8 iconst_1
9 aaload
10 invokestatic #13 <Method int parseInt(java.lang.String)>
13 istore_2
14 getstatic #12 <Field java.io.PrintStream out>
17 new #7 <Class java.lang.StringBuffer>
20 dup
21 ldc #1 <String "Sum = ">
23 invokespecial #10 <Method java.lang.StringBuffer(java.lang.String)>
26 iload_1
27 iload_2
28 iadd
29 invokevirtual #11 <Method java.lang.StringBuffer append(int)>
32 invokevirtual #15 <Method java.lang.String toString()>
35 invokevirtual #14 <Method void println(java.lang.String)>
38 return




7 RELATED COURSES AVAILABLE
INTRODUCTION TO JAVA PROGRAMMING
The aims of this Java training courses is to understand the role that Java plays on the Internet; describe the be....
JAVA (V1.2): ADVANCED PROGRAMMING
This course teaches the reader to learn, understand and become familiar with the advanced features of Java progra....
INTRODUCTION TO JAVABEANS
To go from the fundamentals of JavaBeans programming to the threshold of Advanced level. Gaining in depth progr....
C++ PROGRAMMING
Object oriented programming is fast becoming the leading software design methodology, with C++ becoming ever more....
C PROGRAMMING
This course is design to provide non-C programmers with the essential skills and knowledge necessary to allow the....
CONTACT US
Tuesday 22nd May 2012  © COPYRIGHT 2012 - website design by Website Design by Visualsoft