Exploring the Impact of Java Integer Cache on Performance: A JMH Benchmark Study
Tags: java, performance, learning, development
Published: 2024-05-09 00:53:32

In one of my [posts](https://dev.to/joao9aulo/2-2-5-in-java-21-2341), I made 2 + 2 equal to 5 using Java 21. In summary, the Integer class in Java has a cache for values from -128 to 127. I manipulated these values by swapping the number 4 with 5. This made me ponder the importance of this cache and its impact on a program. So, I decided to conduct a test using JMH, which is a micro benchmark API. In this test, I instantiated an array of Integers of various different sizes, both within and outside the range of cached values, and measured the execution time of each case in nanoseconds.

```
package com.benchmark.integer.cache;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class BenchMark {

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List cached1() {
        return instanciateNumbers(1, true);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List cached100() {
        return instanciateNumbers(100, true);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List cached1000() {
        return instanciateNumbers(1000, true);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List cached10000() {
        return instanciateNumbers(10000, true);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List cached100000() {
        return instanciateNumbers(100000, true);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List cached1000000() {
        return instanciateNumbers(1000000, true);
    }

    ////////////////////////////////////////////////
    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List OutOfRangeCache1() {
        return instanciateNumbers(1, false);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List OutOfRangeCache100() {
        return instanciateNumbers(100, false);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List OutOfRangeCache1000() {
        return instanciateNumbers(1000, false);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List OutOfRangeCache10000() {
        return instanciateNumbers(10000, false);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List OutOfRangeCache100000() {
        return instanciateNumbers(100000, false);
    }

    @Benchmark
    @OutputTimeUnit(TimeUnit.SECONDS)
    @BenchmarkMode(Mode.AverageTime)
    public List OutOfRangeCache1000000() {
        return instanciateNumbers(1000000, false);
    }

    public static List instantiateNumbers(int quantity, boolean minorThan128) {
        List<Integer> numeros = new ArrayList<>();
        if (minorThan128) {
            for (int i = 0; i < 128 && i < quantity; i++) {
                numeros.add(i);
            }
        } else {
            for (int i = 128; i < Integer.MAX_VALUE && numeros.size() < quantity; i++) {
                numeros.add(i);
            }
        }

        return numeros;
    }
}
```

After the JMH runner is executed it generated the following results:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4pa0wwoiiiujpu59gtlx.png)


As we can see, in the cases with 1 and 100 integers, the execution time is similar for both the cached and non-cached cases. But as it grows, the execution time for the non-cached cases doesn't stop increasing. In the cached tests, the execution time remains constant. After this test, my curiosity was satisfied; the integer cache has a significant impact on performance. Please feel free to comment or make any suggestions.

Bonus:

If you want to increase the integer cache you set VM parameter or System property like:

`-Djava.lang.Integer.IntegerCache.high=256`

Github:
https://github.com/joao9aulo/benchmark-integer-cache

Sources:

https://stackoverflow.com/questions/30277106/how-to-increase-the-cache-size-for-integer-object
https://www.baeldung.com/java-microbenchmark-harness