Faster JVM Application Warm Up With Zing

The Java Virtual Machine (JVM) provides a managed runtime environment for the safe deployment of applications with performance that can often exceed that of natively compiled languages like C and C++.  Memory management with garbage collection and adaptive compilation through the use of just in time (JIT) compilation are the two most prominent features.

Although the use of bytecodes and JIT compilation can deliver better peak performance, the warm-up time required to reach that level can be problematic for certain classes of application.

In this post, we’ll look at a set of technologies Azul has developed as part of the Zing JVM to address these limitations.

To start with, let’s look at a typical graph of application performance when running on the JVM.

This graph is not ideal, as the application starts with reduced performance and the JVM takes time to reach its full potential.  The graph can be divided into three distinct sections; let’s look at what is happening inside the JVM for each of these.

Now that we understand the way that JIT compilation works in the JVM, what can be done to reduce its impact on application startup performance?  Azul has developed two technologies to give the Zing JVM the ability to mitigate the warm-up effect.

A common suggestion for how to solve this problem is to let the application run until all frequently used methods have been JIT compiled, then have the JVM write the compiled code to a file.  When the application is restarted, the previously compiled code can be reloaded, and the application will run at the speed it did before it stopped.

This sounds like a good solution but has two significant drawbacks:

Azul’s ReadyNow! technology takes a different approach that ensures full correctness of both the code being executed and the startup sequence of the JVM.

To achieve this ReadyNow! records a profile of a running application.  The profile can be taken at any time so users can decide when their application is performing at the level that they require.  Multiple profile snap-shots can be taken so that a user can select the desired profile to use when restarting the application.

The profile records five pieces of data:

  1. A list of all the classes that have been loaded.
  2. A list of all the classes that have been initialized.
  3. The profiling data gathered during execution of C1 JIT compiled code.
  4. Compilations performed by both the C1 and Falcon JITs.
  5. A list of speculative optimizations that failed and resulted in deoptimizations of code.

When the application is started again, this data is used as advanced knowledge for the JVM to perform the following steps:

All this happens before the application starts execution at the main() entry point.

The effect of this is that when the application starts execution almost all of the hot methods have been compiled with the Falcon JIT.  By using the profiling data, the code can be heavily optimized, and speculative optimizations that are known to work are used (ones that don’t can also be avoided).  Performance starts at a level very close to that when the profile was collected.  Due to some restrictions in how this process works the application typically needs only a few transactions to bring it up to full speed.

This approach does, however, have an impact. The JVM has considerably more work to do in advance of when the application can start processing transactions. 

To reduce this effect, Azul has developed Compile Stashing

As we’ve already seen, it is not possible to simply save compiled code and then reload it when restarting an application.  However, it is possible to save the compiled code and use it, in effect, as a cache.

Bytecodes for methods are combined with saved profiling data so they can be converted into an intermediate representation (IR) used by the compiler.  As the code is compiled, the JIT will make calls to the JVM to help it make decisions about optimizations that it can use.  For example, to determine whether a method can be inlined the JIT must first establish whether the method can be de-virtualized, which requires a query to the JVM.  Once the JIT has completed an analysis of the method, it can compile it with the maximum level of optimization.

This process is fully deterministic.  Given the same method bytecodes and profiling data as input and the same set of queries to the JVM, the output from the JIT compiler will always be identical.

Compile Stashing complements ReadyNow!  In addition to recording a profile, the native code of the currently compiled methods are also written to a file, as well as the queries and responses for the VM callbacks.  When the application is started again, ReadyNow! loads and initializes the classes it can, based on the profile as before.  However, the saved compiled methods are now used as a cache to reduce the need for explicit compilation.  The diagram of the flow of operations is shown below:

ReadyNow! uses the combination of the IR for the method’s bytecodes and queries to the VM used during compilation to determine whether the stored compiled code matches.  If it does, the code can be returned from the Compile Stash.  If, for whatever reason, the inputs do not match the compilation request can be passed to the Falcon JIT, as before.  It is important to note that using this technique does not invalidate any of the requirements of the JVM specification regarding initialization of the application.

Tests have shown that, using Compile Stashing, the compile time required by ReadyNow! can be reduced by up to 80%, as well as reducing the CPU load by up to 60%.

As you can see, ReadyNow! and Compile Stashing address the issue of application warm-up time by recording class loading and profiling data, speculative optimizations that did not work and compiled code.   Using all these pieces when restarting an application can lead to a massive reduction in the time and CPU load required for the application to reach the optimum performance level.

Zing is the JVM that starts fast, stays fast and goes faster.

Ready to Start Using a Better JVM?

Try Zing Free on the Linux Distro of your Choice…

Exit mobile version