从JDK源码理解Java Integer的缓存机制

1. 引入

参考1中给出了一段很有趣的代码如下,运行会输出什么呢?

Integer a1 = 9;
Integer b1 = 9;
System.out.println(a1==b1);//true

Integer x = 396;
Integer y = 396;
System.out.println(x==y);//false

按照常理,a1和b1都由auto-boxing机制,new出了两个不同的对象,那a1就不可能等于b1(reference);x和y同理,应该都输出false才符合“语法逻辑”。但是,答案也很奇怪:第一个println输出的是true,第二个是false,这是为什么呢?

2. Integer的cache

从javadoc中(参考2中valueOf(int i)的说明),我们可以看到Integer有自己设计的缓存机制:

 this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

首先,为什么要看valueOf()的实现呢?因为1中的代码是auto-boxing的机制来new Integer对象的,而auto-boxing就是用valueOf来实现的(详见参考4)。

然后,我们从javadoc的描述中,发现Integer它会对一些常用的int值进行缓存,而不是对任何int值都new一个对象,这样有两个优点:

  1. 节约内存,新建100个值为1的Integer也就只占用一个存储空间
  2. 速度快,每次新建Integer对象都需要花费时间,对常用值的对象就不需要花时间来new对象了

那么,哪些范围内的数值会被缓存呢?javadoc的描述中说默认是-128 到 127。

能从jdk源码中找到缓存相关的这块代码吗?笔者在参考3中找到了,简化加注释如下:

    private static class IntegerCache {
    
    
        static final int low = -128;#最小值默认是-128,这个不能改
        static final int high;
        static final Integer[] cache;

        static {
    
    
            // 最高值默认为127,但也可以修改
            int h = 127;
            high = h;

            int size = (high - low) + 1;
            // 新建一个Integer数组,初始值为-128, -127, ..., 0, 1, 2, ..., 127
            if (archivedCache == null || size > archivedCache.length) {
    
    
                Integer[] c = new Integer[size];
                int j = low;// low=-128
                for(int i = 0; i < c.length; i++) {
    
    
                    c[i] = new Integer(j++);// -128, -127, ..., 0, 1, 2, ...high
                }
                archivedCache = c;
            }
            cache = archivedCache;//cache作为最终缓存数组
        }
    }

    # valueOf()的定义,auto-boxing会调用
    public static Integer valueOf(int i) {
    
    
        if (i >= IntegerCache.low && i <= IntegerCache.high) //low,high的默认值见上面class中的定义
            return IntegerCache.cache[i + (-IntegerCache.low)];//不new object,而是直接从cache中读出object返回
        return new Integer(i);//不在if的区间内,就new object 返回
    }


3. 注意

从上面的JDK源码分析来看,我们知道:

  1. Java的Integer的缓存机制必须在auto-boxing模式下,或者主动调用valueOf(),才会被使用(auto-boxing才会调用valueOf())。
    非auto-boxing模式下,如果不主动调用valueOf(),比如new Integer是绝对不会触发这个缓存机制的。

  2. new对象而不是用auto-boxing机制,是不会调用缓存的,比如

Integer a1 = 9;
Integer b1 = 9;
System.out.println(a1==b1);//true

Integer a2 = new Integer(9);
Integer b2 = new Integer(9);
System.out.println(a2==b2);//false

  1. 如果默认大于127,就不会调用缓存,比如
Integer a3 = 127;//auto-boxing
Integer b3 = 127;//auto-boxing
System.out.println(a3==b3);//true

Integer x = 128;//auto-boxing
Integer y = 128;//auto-boxing
System.out.println(x==y);//false, 因为上界值默认为127(等于127),大于这个值就不会调用cache,而是真实的new对象了
  1. 上界阈值默认为127,但可以通过修改虚拟机参数来改变上限值

具体如何修改呢,比如如下代码保存到Test.java

Integer x = 128;//auto-boxing
Integer y = 128;//auto-boxing
System.out.println(x==y);

编译命令:javac Test.java

运行命令:java -XX:AutoBoxCacheMax=200 Test

如果加上 -XX:AutoBoxCacheMax=200 参数来运行程序,表示可以将Integer的缓存上界值(在auto-boxing模式下)改变为200(默认为127),这样程序输出就是true。
如果不加这个参数,就和上一题一样,输出false。

参考

  1. https://www.geeksforgeeks.org/java-integer-cache/
  2. https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html
  3. https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Integer.java#L1071
  4. https://stackoverflow.com/questions/31445024/does-autoboxing-call-valueof

猜你喜欢

转载自blog.csdn.net/ybdesire/article/details/124975449