Concurrent, low pause collector
The generational collector used by HotSpot is very efficient for collecting short-lived objects and can be tuned to collect young generation (objects created since the last incremental garbage collection) quickly. For example, the setting
-XX:NewRatio=3 means that the ratio between the young and tenured generation is 1:3.
The larger the ratio, the shorter the minor collection time, but (unfortunately) the more frequent the major collections have to be! Java SE 6 HotSpot has some advanced options for working around this problem. Two of these are of particular interest to us:
This first option activates the concurrent mark sweep (CMS) collector (also known as the concurrent low pause collector). This collector attempts to minimize the pauses due to garbage collection by doing most of the garbage collection work concurrently with the application threads. In other words, the VM can take advantage of idle cores to perform garbage collection concurrently. It should be noted that this option is different from
-XX:+UseParallelGC, which uses multiple threads for collection and only executes the collector faster. (Furthermore, the parallel collector cannot be used with the concurrent low pause collector.)
The second option allows CMS to run even if you are not reaching the limits of memory. It prevents CMS from kicking in too late and being unable to finish its collection before available memory is exhausted. Figure 3 is the execution graph of our real-time benchmark with the CMS options activated.
Figure 4 shows the memory usage for this test.
As you see, the worst-case execution time has been reduced by an order of magnitude, but the average execution time has increased.
Ahead-of-time compilation and alternatives
HotSpot does not provide an option to compile classes before execution. It does provide an option to specify the compilation threshold (the number of times a method has to be executed before being compiled). Setting the compilation threshold to one (
-XX:CompileThreshold=1) forces the code to be compiled at first execution. This option is not as good as ahead-of-time compilation (supported by virtual machines such as Excelsior JET) or static native compilation (supported by the Gnu Compiler for Java) due to the performance hit the first time the code is executed. But it is often possible to execute critical code at startup and prevent JIT compilation from occurring at an inappropriate time. Figure 5 shows the benchmark result from using this option in conjunction with the CMS collector.
Because the benchmark code is so small, compiling the code at start-up does not result in a significant reduction in the worst-case execution time. However, JIT compilation for large applications may delay code execution by hundreds of milliseconds.