Integer Caching in JVM

Integer Caching in JVM

We will briefly learn how Kotlin/Java optimizes integer numbers caching

Have a look at this snippet, do you see anything weird?

fun integerCacheDemo() {
    val a: Int = 127
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b

    println(boxedA === anotherBoxedA)               // true
    println(System.identityHashCode(boxedA))        // 434091818   <--|the same
    println(System.identityHashCode(anotherBoxedA)) // 434091818   <--|address

    println(boxedB === anotherBoxedB)               // false
    println(System.identityHashCode(boxedB))        // 398887205
    println(System.identityHashCode(anotherBoxedB)) // 2114889273
}

Let me help, How boxedA and anotherBoxedA are referentially equal but boxedB and anotherBoxedB are NOT?

Let's start from the beginning:

On the JVM, non-nullable values of Integer numbers are represented as primitives, in Kotlin there are no primitives data types, instead, it wraps the primitive value into a Wrapper object like Int, Long, Double, etc.

But what if I have an array of 1000 nullable integer numbers? Here Integer Caching comes to the scene.

Integer caching: is an optimization mechanism to reduce object creation for integer numbers, and it has some rules to get the benefits of this optimization:

  • The integer value must be in the range between -128 to 127.
  • This works only in autoboxing, not explicit object creation.

take a look at the below snippet:

fun integerCacheDemo() { 
    val a: Int = 128    <-- exceeds the range
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b

    println(boxedA === anotherBoxedA) // false
    printAddress(boxedA)              // 434091818   <--|NOT the same
    printAddress(anotherBoxedA)       // 398887205   <--|address

    println(boxedB === anotherBoxedB) // false
    printAddress(boxedB)              // 2114889273
    printAddress(anotherBoxedB)       // 1025799482
}

val a is exceeding the integer caching range, so it creates a new object, therefore, boxedA refers to another object other than anotherBoxedA As long as the number is inside the caching range, it will return the same created object from IntegerCache, as it creates objects from -128 to 127 at the startup, so it does NOT create an object on-demand, and when a declaration of an integer number with a value inside the integer caching range, it will return the reference for the cached object.

Peace ✌️