java基础数据类型自动装箱的缓存

3/8/2017来源:ASP.NET技巧人气:3095

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

问题


public static void main(String[] args) { Integer a = 100, b = 100, c = 1000, d = 1000; 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 >= 127; 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 = -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) { 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); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }

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

在IntegerCache中定义了一个Integer数组cache 在first usage(IntegerCache类被加载)时进行初始化 其缓存范围默认为从-128至127 可以通过-XX:AutoBoxCacheMax=<size>来控制cachesize

所以,在默认情况下,-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 + 128]; } private static final Byte[] VALUES = new Byte[256]; static { for (int i = -128; i < 128; i++) { VALUES[i + 128] = new Byte((byte) i); } }

2.2 short

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

2.3 long

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

3. char

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

public static Character valueOf(char c) { return c < 128 ? SMALL_VALUES[c] : new Character(c); } private static final Character[] SMALL_VALUES = new Character[128]; static { for (int i = 0; i < 128; 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); }

总结


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

参考文档:


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