DEV Community

Cover image for Exploring the Impact of Java Integer Cache on Performance: A JMH Benchmark Study
João Paulo Martins Silva
João Paulo Martins Silva

Posted on

1

Exploring the Impact of Java Integer Cache on Performance: A JMH Benchmark Study

In one of my posts, 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Image description

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

Top comments (0)

Great read:

Is it Time to go Back to the Monolith?

History repeats itself. Everything old is new again and I’ve been around long enough to see ideas discarded, rediscovered and return triumphantly to overtake the fad. In recent years SQL has made a tremendous comeback from the dead. We love relational databases all over again. I think the Monolith will have its space odyssey moment again. Microservices and serverless are trends pushed by the cloud vendors, designed to sell us more cloud computing resources.

Microservices make very little sense financially for most use cases. Yes, they can ramp down. But when they scale up, they pay the costs in dividends. The increased observability costs alone line the pockets of the “big cloud” vendors.

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay