There is still a common belief about Java being slow (especially during startup) and memory-consuming, making it not being the first option for ephemeral service instances, like containers. This blog post pretends to put some light on those assertions, quantifying the impact of a last-generation JVM in a simple, single-threaded, application. We compare the impact of the JVM measuring the execution time and memory spent of a Java QuickSort implementation , comparing it with the execution of a native image generated with the modern GraalVM Ahead-Of-Time (AOT) compiler and the same QuickSort implementation in Go .
Each test consists of a program that:
The tests are repeated for array sizes from 10 to 1_000_000_000 32-bit integers.
The QuickSort program has three implementations:
The image below shows, in logarithmic scale, the execution times (in milliseconds) of the different implementations for the different array sizes:
The scenarios where the array is really small (less than 10_000 elements) allow us to quantify the impact of the JVM spinup time: between 80 and 90 milliseconds.
For scenarios that require more than 10 seconds, the JIT compiler is able to optimize the executable and finishes faster than any other implementation: around 9% faster than AOT, around 2% faster than Go.
The maximum RSS has been measured for the executions of the previous section:
This post shows a dummy experiment with a single-thread, memory-intensive application. We put light on some aspects, and the conclusions here may be an aspect to consider in future decisions (Java or not Java? JIT or AOT?) but definitely, you should do your own benchmarks for your workloads: CPU or I/O intensive? Short-lived or long-lived? Few or many instances of your workload?
The above conclusions are open for discussion.
Startup time should not be the driver for choosing one or another configuration.
Execution speed should not be the driver for choosing one or another configuration.
Memory usage and packaging size MAY BE a driver for choosing one or another configuration.
Java AOT comes at a price.
Go is a very mature language and comes with a mature compiler and toolset. It beats GraalVM AOT compiler in terms of compilation time, execution time and executable size.
The GraalVM native-image generator has still room for improvement, but their current results look promising. In future evaluations, we should see how GraalVM executables work with debuggers, profilers, and other tools.