Mihai Surdeanu

Java – Sometimes strange things happens under the hood

Today, we will continue the series of articles about Java as programming language and we will try to present a case when strange things happens under the hood. As a result, we can end up with different production issues.

Let’s suppose we have the following code in production:

package ro.mihaisurdeanu;

public class Main {

    public static void main(String[] args) {
        Integer a = 100;
        Integer b = 100;
        
        System.out.println(a == b);
    }

}

Can you guess the output printed? Answer: true.

For sure this is not the expected answer. Why? You probably remember the purpose of “==” operator. This operator is used to compare the equality between two object references. If the answer is true, for sure the reference is the same. In other words, a and b points to the same object in memory. Nice!

To understand what happens under the hood, an idea will be to actually see the implementation for static method Integer.valueOf – one pattern for creating a new integer: (OpenJDK 11)

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

Behind the scene there is a nice caching system to avoid creating new instances and to optimize memory usage.

   /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * jdk.internal.misc.VM class.
     */

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer[] cache;
        static Integer[] archivedCache;

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            // Load IntegerCache.archivedCache from archive, if possible
            VM.initializeFromArchive(IntegerCache.class);
            int size = (high - low) + 1;

            // Use the archived cache if it exists and is large enough
            if (archivedCache == null || size > archivedCache.length) {
                Integer[] c = new Integer[size];
                int j = low;
                for(int k = 0; k < c.length; k++)
                    c[k] = new Integer(j++);
                archivedCache = c;
            }
            cache = archivedCache;
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

Please also note that Integer and Long are immutable. Everything between [-128, 127] is cached by default. As you can see, you can also play with some VM properties to extend or reduce the default range.

In fact, this an example of a flyweight pattern. 🙂

Mihai

Pasionat de IT. Pasionat de viață. Pasionat de tot ceea ce înseamnă a face o viață mai bună, plină de înțelegere, ajutor reciproc și iubire de aproape.

Adaugă comentariu

Arhive

Arhiva personală