Java's auto boxing can be problematic
Autoboxing was introduced in Java 1.5. This is a “feature” which allows programmers to mix primitive and boxed primitive types. A boxed primitive type is simply an object representation of the primitive type. For Example:
-
int
=>Integer
-
long
=>Long
-
double
=>Double
With autoboxing, the lines between the primitive and the object are hidden because the compiler automagically boxes (from primitive to object) and unboxes (from object to primitive). The details of which are hidden from the programmer.
Personally I don’t like it as autoboxing can lead to some VERY hard to spot performance issues. Have a look at the following program. Can you spot the problem?
/**
* An example of a class which runs very slowly. Creates objecs unnessesarily.
*/
public class SlowProgram {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
// Very slow
Long slowSum = 0L; //create the object by auto boxing 0
for (long i = 0; i <= Integer.MAX_VALUE; i++ ) {
slowSum += i;
}
long endTime = System.currentTimeMillis();
long durationSlow = (endTime - startTime);
System.out.println("Slow: duration: " + durationSlow + ", sum: " + slowSum);
// much faster
startTime = System.currentTimeMillis();
long fastSum = 0; //create the object by auto boxing 0
for (long i = 0; i <= Integer.MAX_VALUE; i++ ) {
fastSum += i;
}
endTime = System.currentTimeMillis();
long durationFast = (endTime - startTime);
System.out.println("Fast: duration: " + durationFast + ", sum: " + fastSum);
System.out.println("Fast code is : " + (durationSlow - durationFast) + "ms faster ");
}
}
Although the program calculates the correct answer in both instances block 1 is MUCH slower than block 2. In fact, block 2 is 89.8% faster than block 1. Let’s have a look at the decompiled code to see what Java does to our original code and why this program is a performance hog.
/**
* Decompiled byte code.
*/
public class SlowProgram {
public SlowProgram() {
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
Long slowSum = Long.valueOf(0L);
long i;
for(i = 0L; i <= 2147483647L; ++i) {
slowSum = Long.valueOf(slowSum.longValue() + i);
}
i = System.currentTimeMillis();
long durationSlow = i - startTime;
System.out.println("Slow: duration: " + durationSlow + ", sum: " + slowSum);
startTime = System.currentTimeMillis();
long fastSum = 0L;
long i;
for(i = 0L; i <= 2147483647L; ++i) {
fastSum += i;
}
i = System.currentTimeMillis();
long durationFast = i - startTime;
System.out.println("Fast: duration: " + durationFast + ", sum: " + fastSum);
System.out.println("Fast code is : " + (durationSlow - durationFast) + "ms faster ");
}
}
Thanks to the smartness of the JVM, it is doing some automatic object creation for us. Notice how the first loop creates a new Long
object on each iteration? The second loop simply works directly with the primitive.
Through a simple typing error, we have created 2,305,843,008,139,952,128 (231) new long instances. Although creating new objects is generally cheap, creating unnecessary objects as this program has done should be avoided.
« Calculating the median of a time series
Monoliths, Micro Services and Service Oriented Architectures »