天天看点

java基础数据类型自动装箱的缓存问题原因分析总结参考文档:

从Java 5开始,基础数据类型被赋予了自动装箱和自动拆箱机制。

问题

public static void main(String[] args) {
    Integer a = , b = , c = , d = ;
    System.out.print(a == b);//true
    System.out.print(',');
    System.out.println(c == d);//false
}
           

打印结果为

true,false

这是为什么呢?

原因

这是由于Integer在自动装箱过程中使用了缓存机制。

基础数据类型在自动装箱过程中会使用缓存机制来提高效率,在缓存范围内相同值的自动装箱对象相同(==为true),本文将总结基础数据类型在自动装箱过程中的缓存特点。

话不多说,先上结果:

类型 字节数 包装类型 缓存范围 说明
byte 1 Byte -128 ~ 127 全部缓存
short 2 Short -128 ~ 127 部分缓存
int 4 Integer -128 ~ 127

部分缓存

最大值默认为127

可以通过设定JVM启动参数

-XX:AutoBoxCacheMax=<size>

来修改缓存的最大值
long 8 Long -128 ~ 127 部分缓存
char 2 Character 0 ~ 127 部分缓存
boolean 1或4 Boolean true, false

全部缓存

boolean类型在编译后使用java虚拟机的int数据类型代替,boolean数组则被编码为byte数组

float 4 Float 没有缓存
double 8 Double 没有缓存

分析

我们知道,在自动装箱过程中,使用了对应包装类的

valueOf

静态方法

例如:

Integer num = 100;

等价于

Integer num = Integer.valueOf(100);

1. int

Integer中自动装箱使用的valueOf函数定义如下:

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

可以看到在IntegerCache.low ~ IntegerCache.high之间的数字,将会从缓存中获取,在这个区间之外的数字,将创建一个新的Integer对象。

接下来再来看看IntegerCache的定义(它是定义在Integer内部的一个静态类):

/**
 * 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 -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
 * sun.misc.VM class.
 */
private static class IntegerCache {
    static final int low = -;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = ;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            int i = parseInt(integerCacheHighPropValue);
            i = Math.max(i, );
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - (-low) -);
        }
        high = h;

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

    private IntegerCache() {}
}
           

通过代码及注释,我们可以了解到以下几点信息:

  • 在IntegerCache中定义了一个Integer数组

    cache

  • 在first usage(IntegerCache类被加载)时进行初始化
  • 其缓存范围默认为从-128至127
  • 可以通过

    -XX:AutoBoxCacheMax=<size>

    来控制

    cache

    size

所以,在默认情况下,-128~127范围内的自动装箱使用了缓存,相同的值自动装箱后的对象相同(即a == b 为true)。

延伸阅读: 为什么java中要做IntegerCache这种设置?

2. byte/short/long

byte/short/long对应的包装类的缓存方式与Integer不同,没有用到自定义Cache类,而是直接在包装类内部定义了一个长度为256的缓存数组:

2.1 byte

public static Byte valueOf(byte b) {
    return VALUES[b + ];
}
private static final Byte[] VALUES = new Byte[];
static {
    for (int i = -; i < ; i++) {
        VALUES[i + ] = new Byte((byte) i);
    }
}
           

2.2 short

public static Short valueOf(short s) {
    return s < - || s >=  ? new Short(s) : SMALL_VALUES[s + ];
}
private static final Short[] SMALL_VALUES = new Short[];
static {
    for (int i = -; i < ; i++) {
        SMALL_VALUES[i + ] = new Short((short) i);
    }
}
           

2.3 long

public static Long valueOf(long v) {
    return  v >=  || v < - ? new Long(v) : SMALL_VALUES[((int) v) + ];
}
private static final Long[] SMALL_VALUES = new Long[];
static {
    for (int i = -; i < ; i++) {
        SMALL_VALUES[i + ] = new Long(i);
    }
}
           

3. char

char的包装类型Character的自动装箱缓存方式类似于byte/short/long,不过缓存长度只有128(0~127)

public static Character valueOf(char c) {
    return c <  ? SMALL_VALUES[c] : new Character(c);
}
private static final Character[] SMALL_VALUES = new Character[];
static {
    for (int i = ; i < ; i++) {
        SMALL_VALUES[i] = new Character((char) i);
    }
}
           

4. boolean

boolean只有2个值:true和false,在源码中valueOf方法使用了2个常量来进行自动装箱

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}
           

5. float/double

float和double都属于浮点型,自动装箱没有使用缓存,任何相同的值自动装箱后用==来判断都会返回false

5.1 float

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

5.2 double

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

总结

在使用基础数据类型时,我们需要注意自动装箱及自动拆箱的影响。特别是在使用==来进行判断的时候要注意自动装箱的缓存

参考文档:

  1. 官方文档 Primitive Data Types
  2. 你真的知道Java中boolean类型占用多少个字节吗?
  3. Java 7之基础类型第1篇 - Java数据类型