Is autoboxing wrecking your Java application's performance? 🤔
While working on the instructional regarding how to convert an array to an ArrayList in Java I noticed that the performance of the call to the Google Guava newArrayList method appeared to be quite slow — this was a mistake, and the reason has to do with Java autoboxing performance — we’ll go over this here along with Java unboxing performance.
Java Autoboxing and Unboxing Performance TOC
Note that Java autoboxing is the same as automatic boxing and I use these words interchangeably in this document.
Summary of Findings
Below I’ve included the combined chart so you can compare the performance results for boxing and unboxing operations in Java.
While unboxing has a bit of a performance hit associated with it, it’s on par with the performance of the example where unboxing is not required.
What should be abundantly clear from this chart is that autoboxing in Java is an expensive operation.
This makes sense since when an autoboxing operation takes place Java primitives are wrapped in the object wrapper equivalent which requires both time as well as memory.
We’re going to focus on the performance when autoboxing in Java in this document and save the memory requirements for another article.
The remainder of this article includes how this issue was discovered along with the evidence that supports this finding.
An errant performance test which included Java autoboxing
As mentioned in the introduction, the Google Guava newArrayList method is, in fact, not slow — rather I was testing using an array of primitive integers and that caused the Java compiler to autobox these values into their wrapper object equivalent — it was the process of autoboxing that was interfering with the performance test.
Autoboxing, in particular, is an expensive operation in Java and can have a considerable impact on an application’s performance.
We can see the source code for the Google Guava Lists newArrayList method on the right side of this page — note that there’s nothing particularly interesting about how this method converts an array into an ArrayList in Java.
The performance penalty when the primitive integer was autoboxed into an instance of java.lang.Integer may have been missed if the size of the array was small enough or if I was only doing this to one variable.
In this case, I set up an example using an array of size 10,000, which is large enough to expose the issue.
In the next section we’ll take a look at the Java autoboxing performance along with the tests that were developed to show the cost of performing this operation.
Java Boxing Performance
Below I’ve included a chart that demonstrates the performance between the two Java applications — one where boxing is used and the other where boxing is not present.
Note that the Java example that requires boxing takes so much time to execute that we almost don’t see the performance of the example where boxing is not needed.
In the next two sections we’ll take a look at the evidence backing these numbers.
Java Performance: With Autoboxing
The performance test that I developed to examine how the Google Guava Lists newArrayList method can be seen below.
Note that line #28 is taking an array of primitive integer values and this is where the autoboxing is taking place.
The performance monitor starts on line #24 so the autoboxing happens during the performance test and this produces numbers which are not accurate.
In this example we can see that the average time spent converting the array to an ArrayList in Java five times appears to be rather excessive at 282662.2 milliseconds (that’s 282.66 seconds or 4.711 minutes)!
On the right I’ve included a similar Java 8 autoboxing performance example to the Groovy script above however I’ve changed the performance test such that it can run in the TIO interpreter, which has a timeout of 60 seconds and does not allow for 3rd party dependencies to be included when example code is executed.
Below I’ve included the full working Java 8 autoboxing performance test which uses Java components and is written using the Groovy programming language.
Note that line #16 creates an array of primitive integers and line #19 adds random primitive integer values to that array.
You should be able to run this script as-is by pasting it into the groovyConsole.
On the right is a link to the GitHub gist for the Java autoboxing performance test source code (this is, in fact, written using the Groovy scripting language however I make an effort to explicitly utilize Java components).
Java Performance: Without Autoboxing
Below I’ve included the example Groovy script which contains adjustments such that the autoboxing performance penalty is not present in this test.
You should be able to paste this script into the Groovy Console and run it without any changes required.
Pay particular attention to lines #16 and #19, where we create an array of type java.lang.Integer, which is populated by instances of java.lang.Integer — there are no primitives used here!
In this example we can see that the average time spent converting the array to an ArrayList in Java five times appears to be rather impressive when compared with the previous example at 2067.2 milliseconds (that’s 2.0672 seconds or 0.0344533 minutes)!
This difference is substantial and clearly demonstrates the performance problem that can come from autoboxing in Java.
Modifying just two lines of code to eliminate autoboxing in Java resulted in a 99.27% improvement in performance.
Below I’ve included the full working Java 8 performance test which uses Java components and which is written using the Groovy programming language.
Note that line #16 creates an array of type java.lang.Integer and line #19 adds random java.lang.Integer values to that array — there is no need for autoboxing on line #28.
You should be able to run this script as-is by pasting it into the groovyConsole.
On the right I’ve included a similar Java 8 performance example which relies on values of type java.lang.Integer juxtaposed with primitive integers and hence does not require any autoboxing.
In this Groovy script I’ve changed the performance test such that it can run in the TIO interpreter, which has a timeout of 60 seconds and does not allow for 3rd party dependencies to be included when example code is executed.
We’ll take a look at unboxing performance in Java in the next section.
Java Unboxing Performance
The performance between converting a list of java.lang.Integer objects to an array of the same and converting a list of java.lang.Integer objects into an array of primitive integers, which requires unboxing is very close to the same in terms of milliseconds.
We can compare the performance of the two operations in the following chart.
The lesson we can draw out of this section on unboxing is clear: when it comes to performance, we need to be careful when utilizing autoboxing, in particular, as that can have a significant impact on the performance of the application.
Java performance example where unboxing is required
In the image below the block of code between lines 36 and 40 is where we should focus out attention.
Line #38 is where the unboxing happens.
The outerCtr loop is here for the purposes of exacerbating any performance differences since these two operations (with and without unboxing) are very fast.
In this example script unboxing is required when converting from an Integer to a primitive int on line 38.
This script was executed five times and has an average execution time of 43549.0 ms (43.5 seconds).
Unboxing, in this case, does not appear to be an expensive operation as the primitive value already exists inside the Integer object and can be read quickly.
Unboxing, in this case, is not required since we’re moving a reference to an Integer from an ArrayList to an Array.
I’m not sure why this operation appears to be slightly slower than the one which requires unboxing — in fact I’d expect the previous example to take slightly longer than this one.
This concludes the section on unboxing performance in the Java Programming Language — the article conclusion follows.
Article Conclusion
In conclusion, while autoboxing in Java offers a convenient bridge between primitive types and their corresponding wrapper classes, it’s clear that this convenience comes with a potentially significant performance penalty.
The benchmarks and examples discussed in this article underscore the importance of being mindful of boxing and unboxing operations, especially as it pertains to applications where performance is especially important.
By making simple modifications to our code, such as avoiding unnecessary autoboxing, in particular, we can achieve significant improvements in execution speed and efficiency.
Software engineers need to understand the underlying mechanisms of the Java language features we use, ensuring that our applications are not only functional but also optimized for speed.
The case study presented here demonstrates that with careful consideration and minor adjustments, we can overcome the pitfalls of autoboxing and significantly enhance our Java applications’ performance.
Finally, note that Project Valhalla should improve the performance of the Java Programming Language and the introduction of value types should help with autoboxing performance, in particular (see also Project Valhalla on Wikipedia and on GitHub).
If you’re looking to hire a Temporary CTO then I might be able to help you — schedule an appointment with me today and we’ll discuss your requirements in more detail.
See Also
Frequently Asked Questions (FAQ)
This section includes frequently asked questions about autoboxing and auto unboxing as it pertains to the Java Programming Language.Autoboxing (automatic boxing) in the Java Programming Language is a feature whereby the Java compiler automatically converts between the primitive types and their corresponding object wrapper classes — for example, converting an int to an java.lang.Integer, a double to a java.lang.Double, etc., without requiring explicit code to do so.
This feature simplifies the coding process by allowing programmers to mix primitive and wrapper types, relying on the compiler to add conversion code where necessary.
Unboxing in the Java programming language is a feature whereby the Java compiler automatically converts between primitive object wrapper classes and their corresponding primitive type — for example converting from an instance of java.lang.Integer to an int, an instance of java.lang.Double to a double, etc, without requiring that code be written to perform this operation.
The Autoboxing feature was introduced into the Java Programming Language with the release of Java 1.5 (AKA Java 5 / Java SE 5.0) in September 2004.