- 59 - remember that performance is partly user expectation. If you tell your user that VM A gives such
and such a performance for your application, but VM B gives this other much slower performance, then you at least inform your user community of the implications of their choice of
VM. This could also possibly put pressure on vendors with slower VMs to improve them.
3.3.2 VMs with JIT Compilers
The basic bytecode interpreter VM executes by decoding and executing bytecodes. This is slow, and is pure overhead, adding nothing to the functionality of the application. A just-in-time JIT
compiler in a virtual machine eliminates much of this overhead by doing the bytecode fetch and decode just once. The first time the method is loaded, the decoded instructions are converted into
machine code native for the CPU the system is running on. After that, future invocations of a particular method no longer incur the interpreter overhead. However, a JIT must be fast at
compiling to avoid slowing the runtime, so extensive optimizations within the compile phase are unlikely. This means that the compiled code is often not as fast as it could be. A JIT also imposes a
significantly larger memory footprint to the process.
Without a JIT, you might have to optimize your bytecodes for a particular platform. Optimizing the bytecode for one platform can conceivably make that code run slower on another platform though
a speedup is usually reflected to some extent on all platforms. A JIT compiler can theoretically optimize the same code differently for different underlying CPUs, thus getting the best of all
worlds.
In tests by Mark Roulo http:www.javaworld.comjavaworldjw-09-1998jw-09-speed.html
, he found that a good JIT speeded up the overhead of method calls from a best of 280 CPU clock cycles
in the fastest non-JIT VM, to just 2 clock cycles in the JIT VM. In a direct comparison of method call times for this JIT VM compared to a compiled C++ program, the Java method call time was
found to be just one clock cycle slower than the C++: fast enough for almost any application. However, object creation is not speeded up by anywhere near this amount, which means that with a
JIT VM, object creation is relatively more expensive and consequently more important when tuning than with a non-JIT VM.
3.3.3 VM Startup Time
The time your application takes to start depends on a number of factors. First, there is the time taken by the operating system to start the executable process. This time is mostly independent of the VM,
though the size of the executable and the size and number of shared libraries needed to start the VM process have some effect. But the main time cost is mapping the various elements into system
memory . This time can be shortened by having as much as possible already in system memory. The most obvious way to have the shared libraries already in system memory is to have recently started
a VM. If the VM was recently started, even for a short time, the operating system is likely to have cached the shared libraries in system memory, and so the next startup is quicker. A better but more
complicated way of having the executable elements in memory is to have the relevant files mapped onto a memory resident filesystem; see
Section 14.1.3 in
Chapter 14 , for more on how to manage
this. The second component in the startup time of the VM is the time taken to manage the VM runtime
initializations . This is purely dependent on the VM system implementation. Interpreter VMs generally have faster startup times than the JIT VMs, because the JIT VMs need to manage extra
compilations during the startup and initial classloading phases. Starting with JDK 1.3, Sun is trying to improve the startup time imposed by the VM. VMs are now already differentiated by their startup
times; for example, the JDK 1.3 VM has a deliberately shortened startup time compared to JDK 1.2.
- 60 - HotSpot has the more leisurely startup time acceptable for long-running server processes. In the
future you can expect to see VMs differentiated by their startup times even more.
Finally, the application architecture and class file configuration determine the last component of startup time. The application may require many classes and extensive initializations before the
application is started, or it may be designed to start up as quickly as possible. It is useful to bear in mind the user perception of application startup when designing the application. For example, if you
can create the startup window as quickly as possible and then run any initializations in the background without blocking windowing activity, the user will see this as a faster startup than if
you waited for initializations to finish before creating the initial window. This design takes more work, but improves startup time.
The number of classes that need to be loaded before the application starts are part of the application initializations, and again the application design affects this time. In the later section
Section 3.8 , I
discuss the effects of class file configuration on startup time. Section 13.3
, also has an example of designing an application to minimize startup time.
3.3.4 Other VM Optimizations