Virtual Machines and Other Overhead
A lot of criticisms are leveled at Java as a language. In general, the critics fall into two categories:
- People complaining that Java is not Smalltalk
- People complaining that Java is not C++
A lot of people complain about the overhead of Java bytecode being interpreted. This argument isn’t entirely fair, for two reasons. The first is that Java doesn’t have to be interpreted at all—implementations such as GCJ compile it directly to machine code, and use a runtime library in the same way that languages such as Objective-C work. The other reason is that using a virtual machine doesn’t necessarily translate to overhead.
Research on dynamic recompilers such as Dynamo in 2000 and earlier showed that a just-in-time recompiler, running MIPS code on a MIPS machine, could give a 10–20% speed increase over running the same code on the raw hardware. The reason for this improvement is that a virtual machine could perform some categories of optimization that weren’t available to a static compiler.
Consider the case of function inlining. Although SPARC is something of an exception to this rule, function calls on most architectures are relatively expensive; you have to save the current register set on the stack and then do the same operation in reverse later. For small functions, the time spent performing the function call can be greater than the time spent inside the function. Due to the way C works, it’s impossible for the compiler to inline a function defined in another source file. Both source files are compiled to binary object files independently, and these are linked.
A just-in-time compiler doesn’t have these limitations. It can even inline functions defined in the standard library, because the inlining won’t be preserved between program runs, allowing the library to be updated without rebuilding the application.
To give some idea of how important this one optimization can be, I increased speed in a C program by more than 25% last year simply by moving some commonly used functions into header files where the compiler could inline them.