7. CONCLUSION
Java, as a programming language, offers enormous potential by providing platform independence and by combining a wide variety of language features found in different programming paradigms, such as, an object-orientation model, multithreading, automatic garbage collection, and so forth. The same features that make Java so attractive, however, come at the expense of very slow performance. The main reason behind Java’s slow performance is the high degree of hardware abstraction it offers. To make Java programs portable across all hardware platforms, Java source code is compiled to generate platform-independent bytecodes. These bytecodes are generated with no knowledge of the native CPU on which the code will be executed, however. Therefore, some translation or interpretation must occur at run-time, which directly adds to the application program’s execution time.
Memory management through automatic garbage collection is an important feature of the Java programming language since it releases programmers from the responsibility for deallocating memory when it is no longer needed. However, this automatic garbage collection process also adds to the overall execution time. Additional overhead is added by such features as exception handling, multithreading, and dynamic loading.
In the standard interpreted mode of execution, Java is about 10–50 times slower than an equivalent compiled C program. Unless the performance of Java programs becomes comparable to compiled programming languages such as C or CCC, however, Java is unlikely to be widely accepted as a general-purpose programming language. Consequently, researchers have been developing a variety of techniques to improve the performance of Java programs. This paper surveyed these alternative Java execution techniques. Although some of these techniques (e.g., direct Java compilers) provide performance close to compiled C programs, they do so at the possible expense of the portability and flexibility of Java programs. Java bytecode-to-source translators convert bytecodes to an intermediate source code and attempt to improve performance by applying existing optimization techniques for the chosen intermediate language. Some other techniques attempt to maintain portability by applying standard compiler optimizations directly to the Java bytecodes. Only a limited number of optimization techniques can be applied to bytecodes, though, since the entire program structure is unavailable at this level. Hence, bytecode optimizers may not provide performance comparable to direct compilation.
Another technique to maintain portability while providing higher performance is to parallelize loops or recursive procedures. However, the higher performance of such techniques is obtained only in multiprocessor systems and only on application programs that exhibit significant amounts of inherent parallelism. Yet another approach to high performance Java is a Java processor that directly executes the Java bytecodes as its native instruction set. Although these processors will execute Java programs much faster than either interpreters or compiled programs, they are of limited use since applications written in other programming languages cannot be run efficiently on these processors.
The current state-of-the-art research in Java execution techniques is being pursued along several directions. To match the performance of compiled programs, Java bytecodes must be translated to native machine code. However, the generation of this native machine code must be done dynamically during the program’sexecution to support Java’s portability and flexibility. Researchers are continually improving JIT compilation techniques to generate more highly optimized code within the limited amount of time available for the JIT compilation. Dynamic compilation techniques based on program execution profiles are also being applied. These techniques dynamically generate highly optimized native code as the program is executed. This dynamic compilation allows the program execution to adapt itself to its varying behavior to thereby provide improved performance.
Another trend for obtaining greater levels of performance is to improve the performance of the various individual JVM features, such as garbage collection, thread synchronization, and exception handling, that add overhead directly to the execution of the native code. Yet another trend is to execute Java programs in parallel and distributed environments. Different parallelization models are being developed to extract parallelism from serial Java programs without modifying the underlying JVM implementation. The bottleneck in this case, however, is the performance of the Java RMI and multithreading mechanisms, although research is also being pursued to provide efficient implementations of Java RMI and thread support mechanisms that will improve the performance of parallel and distributed Java programs.
The choice of a particular Java execution technique must be guided by the requirements of the application program as well as the performance offered by the technique. However, as was pointed out earlier, the performance evaluation of these various execution techniques is incomplete due to the lack of a standardized set of Java benchmark programs. Also, many of the techniques that have been evaluated were not complete implementations of the JVM. Some included garbage collection and exception handling, for instance, while others did not. These methodological variations make it extremely difficult to compare one technique to another even with a standard benchmark suite.
While Java has tremendous potential as a programming language, there is a tremendous amount yet to be done to make Java execution-time performance comparable to more traditional approaches.
ACKNOWLEDGMENTS
We thank Amit Verma and Shakti Davis for their help in gathering some of the information used in this paper.