深入理解Integer等类的缓存机制

值类型和引用类型

  首先简单回顾一下Java的值类型和引用数据类型:详情请看我的这篇博客
值类型:Java的八种基本数据类型boolean、long、int、short、byte、float、double、char都是值类型,对于值类型变量是直接在内存中分配的,栈空间直接存储了它们的值,比如下面的int型变量a和b。
引用类型:Java引用类型继承于Object类,像八种基本数据类型的包装类,以及我们自定义的类都属于引用类型。对于&引用类型变量的空间分配需要使用到栈和堆,首先在栈中分配“引用”,然后在堆中存储对象本身的值。
  我们知道Java中的"=="是直接比较内存地址的。因此,对于下面的代码,我们很容易知道a == b,i1 == i2分别为true,false。然而i3 == i4却返回了true,这是为什么呢?这其实是Java的缓存机制。

		int a = 1;
        int b = 1;
        Integer i1 = 128;
        Integer i2 = 128;
        Integer i3 = 127;
        Integer i4 = 127;
        System.out.println(a == b);
        System.out.println(i1 == i2);
        System.out.println(i3 == i4);


缓存机制

Integer

  Java为了使得八种基本数据类型具有面向对象的性质,增添了其相应的包装类。而装箱、拆箱则是为了方便它们之间的互相转化,jdk1.5后,编译器实现了自动装箱和拆箱。
装箱:基本类型封装为包装类型。自动装箱(通过valueOf方法实现),比如Integer i1 = 128;
拆箱:包装类型简化为基本类型。自动拆箱(通过xxvalue方法实现),比如int a = new Integer(100);

  从jdk1.5开始,Java为了节省内存,提高性能,为Integer等包装类(除了Float和Double两种浮点类型)增加了缓存机制。即当变量的值在一定范围时,变量将会共用一个对象。除了Integer类,其他所有整数类型的类都有类似的缓冲机制。下面是类的缓存范围:
Integer: 128 -128 128~ 127 127 127
Byte: 128 -128 128~ 127 127 127
Short: 128 -128 128~ 127 127 127
Long: 128 -128 128~ 127 127 127
Character: 0 0 0~ 127 127 127
,即常用的ASCii码
  再回到上面,i3和i4为127,均在缓存范围里面,因此,对于i3和i4,此时他们均指向堆内存中的同一个Integer对象实例,因此返回true。
  但这种缓存机制只适用于自动装箱,比如i5是new生成的对象,此时缓存机制不起作用,i3==i5返回了false。

		Integer i3 = 127;
        Integer i4 = 127;
        Integer i5 = new Integer(127);


再来看一下缓存机制是如何实现的,以及为什么只有在自动装箱时才起作用?


缓存机制实现

  当编译器检测到需要将基本数据类型转换成包装类型时,自动调用valueOf方法进行自动装箱。可以看到在Integer中的valueOf方法中,如果值处在缓存范围(IntegerCache.low~IntegerCache.high)时,则直接返回内部类IntegerCache中静态成员cache数组中的Integer对象,否则返回new生成的对象,这也是为什么缓存机制只在自动装箱时才起作用。

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

  接下来看一下Integer的内部类IntegerCache的代码,相当于一个工具类。可以看到缓存下界low为-128,上界high可以通过设置虚拟机相关参数来改变(但最小为127),然后根据上下界声明并且初始化了一个Integer数组cache,在valueOf方法中以直接返回此cache中的Integer对象。

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

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.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;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

对于其他包装类也有类似的缓存内部类,比如CharacterCache、LongCache等,在此就不再赘述了。

全部评论

相关推荐

03-25 17:53
已编辑
门头沟学院 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务