Java装箱类缓存分析

Integer

Integer内部有一个IntegerCache类,这个类用来缓存int型数值,默认缓存的范围是-128 ~ 127,如果设置了系统属性java.lang.Integer.IntegerCache.high,则获取这个系统属性,之后进行比对:

i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);

正常情况下,上限要么还是127,要么就是java.lang.Integer.IntegerCache.high, 如果该系统属性比Integer.MAX_VALUE - 128 - 1还大, 那么上限就是Integer.MAX_VALUE - 128 - 1(有谁会那么蛋疼的把上限设这么高?.), 确定好上限后,会初始化内部的缓存数组:

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

预先放入-128 ~ 127范围内的Integer对象

当调用Integer.valueOf(int)时, 会首先判断传入的int值是不是在-128127范围内,如果从`ImageCache`中直接获取`Integer`对象,这样避免了重新创建一个Integer的内存开销;如果不在-128127范围内,那么就直接生成一个新的Integer, 代码如下:

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

Integer内部有缓存,那么同样都是装箱对象的Double,Long等类型会有缓存吗?

Byte

    private static class ByteCache {
        private ByteCache(){}

        static final Byte cache[] = new Byte[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Byte((byte)(i - 128));
        }
    }
    
    public static Byte valueOf(byte b) {
        final int offset = 128;
        return ByteCache.cache[(int)b + offset];
    }

Byte是基础类型byte的装箱类,一个byte代表的是一个字节,刚好是-128 ~ 127,所以Byte装箱类缓存了byte可能的所有值

Short

  private static class ShortCache {
        private ShortCache(){}

        static final Short cache[] = new Short[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Short((short)(i - 128));
        }
    }
    
    public static Short valueOf(short s) {
        final int offset = 128;
        int sAsInt = s;
        if (sAsInt >= -128 && sAsInt <= 127) { // must cache
            return ShortCache.cache[sAsInt + offset];
        }
        return new Short(s);
    }


可以看到Short也是缓存了-128 ~ 127范围内的值

Long

   private static class LongCache {
        private LongCache(){}

        static final Long cache[] = new Long[-(-128) + 127 + 1];

        static {
            for(int i = 0; i < cache.length; i++)
                cache[i] = new Long(i - 128);
        }
    }
    
    public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }
    

Long内部也使用了缓存,范围也同样是-128 ~ 127

Double、Float

    public static Float valueOf(float f) {
        return new Float(f);
    }
    
    public static Double valueOf(double d) {
        return new Double(d);
    }
    

可以看到, Double, Float内部并没有使用缓存,而是每次都是直接生成一个新的对象

为什么这两个类没有做缓存呢?因为没意义……这两个类代表的都是浮点数,从Integer, Short,Byte中可以看到,缓存至多存256个数,缓存太多反而会占用更多内存,而浮点数是包含小数点后面的数,如果要缓存256个数,应该怎么选范围呢?还是-128~127?那126.01怎么办,没有缓存到啊,126.10也没有缓存到,所以对于浮点数来说缓存命中率太低,还不如不缓存,省的浪费内存

推荐阅读更多精彩内容