The growing excitement about Java in corporate IT departments has been closely followed by a growing concern about its performance. While numerous trade seminars, presentations and articles have explored Java performance, very few have focused on real world IT systems. This article discusses the performance of Java-based applications that solve real IT problems: enforcing business rules, accessing disparate data, and presenting information graphically.
To determine Java's performance impact in solving each of the above problems, we developed a variety of stand-alone and distributed object applications in both Java and C++, and compared their throughput under various load conditions. We also applied the latest performance enhancing techniques available in both languages such as threads, and Just-In-Time compilation and measured their impact. Our findings are both surprising and contrary to some widely held notions about Java's performance.
- Loading program executables: either from local storage (hard disk) or remotely over the network
- Running program instructions, including math operations, method calls and other business logic.
- Allocating and freeing memory used by the application.
- Accessing system resources such as I/O, file handling, and printing.
- the above, we would add the following tasks for a typical GUI-based IT application:
- Rendering and managing a Graphical User Interface (GUI)
- 6. Handling user events (mouse clicks, input, drag-and-drop, etc).
From the above diagram it can be seen that in addition the 6 sets of functions described for a C++ application, a Java applications also:
7. Runs a Byte-code verifier, and Security Manager during program loading to prevent illegal stack overflow and data conversion and to restrict access to resources such as network sockets. This would seem to indicate that a Java application would load more slowly than an equivalent C++ one, but in practice, there are 2 reasons why Java applications often load faster, especially across a network. Java executables are significantly smaller in size than their C++ counterparts. The class files which make up a Java application can be loaded dynamically as needed rather than being loaded all at once, as is often the case with C++ libraries.
8. Uses either a Byte-code interpreter or a Just-In-Time (JIT) compiler to translate byte-code to machine instructions before executing them. If each program instruction is interpreted and then run, the application will perform 3-10 times slower than a compiled C++ version. However, JIT compilers significantly reduce the performance lag by compiling often-used instructions into machine code on the fly.
They also perform some primary and secondary optimizations on the code, similar to, but not as extensive as the optimization done by a good C++ compiler. We would therefore expect program instructions to be executed fastest by a C++ program, with a JIT-enabled Java VM close behind and a Java interpreter performing much slower.
9. Performs garbage collection by identifying and releasing memory that is no longer in use and moving memory around to prevent fragmentation. Garbage collection can reduce one of the leading causes of bugs in IT systems, memory leaks. However, because it entails using handles for object references and requires the garbage collector to be constantly running in the background, it produces a performance penalty.
10. Uses peer interfaces for accessing system resources, rather than directly calling the underlying operating system. In the case where these peer interfaces map directly to the underlying subsystem (print, I/O, graphic) there is minimal overhead associated with this technique, rather, it maintains a consistent interface to system resources across platforms. However, where the peer interfaces don’t directly use the underlying subsystem, execution time greatly increases.