前言
我们在java面试中基本都遇到过一个关于Integer缓存的问题,类似下面这样:
public static void main(String[] args) { Integer value1 = Integer.valueOf(128); Integer value2 = Integer.valueOf(128); System.out.println(value1 == value2); } 复制代码
上述代码中,打印的结果是true
还是false
?如果把代码改成下面这样呢,结果是否会改变?
public static void main(String[] args) { Integer value1 = Integer.valueOf(127); Integer value2 = Integer.valueOf(127); System.out.println(value1 == value2); } 复制代码
先把正确答案公布一下,第一段代码结果输出false
,第二段代码结果输出true
。
根本原因
为了搞明白里面的根本原因,我们需要深入源码看一下:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } 复制代码
上面的代码逻辑如下:
1.如果i的范围在IntegerCache.low和IntegerCache.high之间,就从IntegerCache中取值;
2.否则就创建一个新的Integer对象;
再深入查看一下IntegerCache这个类:
private static class IntegerCache { // 默认最小值; static final int low = -128; // 最大值 static final int high; // 数组缓存,用于缓存Integer对象; static final Integer cache[]; static { // 默认最大值 int h = 127; // 从虚拟机配置从读取最大值 String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); // 如果虚拟机中配置了最大值,那么就以虚拟机的最大值优先 if (integerCacheHighPropValue != null) { try { // 把最大值转换成int int i = parseInt(integerCacheHighPropValue); // 如果比127大,就作为最大值;否则127作为最大值 i = Math.max(i, 127); // 计算缓存数组的长度,需要预留129个位置,因为最小值是固定-128,再加上0,所以是129个位置,另外的就是最大值的长度; h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } // 计算出来的最大值赋值给high high = h; // 计算出缓存数组的大小,并创建出数组对象 cache = new Integer[(high - low) + 1]; // 准备循环填充缓存数组 int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // 断言最大值是否大于等于127; assert IntegerCache.high >= 127; } // 私有构造函数,不允许其他类创建这个IntegerCache,只能再Integer内部使用; private IntegerCache() {} } 复制代码
根据上述源码,也就是说,IntegerCache默认的缓存范围是在[-128,127];只要我们给出的数值范围在[-128,127]内,就会使用到IntegerCache提前创建好的Integer对象,返回的结果全都是true;超出这个范围就只能通过new Integer(i)
构建新的对象,返回的结果就是false;
如何修改默认最大值
但是在代码中,我们观察到了最大值是可以从虚拟机的配置参数中获取的,那么是不是意味着我们可以通过修改虚拟机配置参数来修改这个默认最大值呢?答案是可以的!
我们在启动jvm时带上以下参数就可以达到改变缓存范围的目的:
-XX:AutoBoxCacheMax=<size> 复制代码
比如我们指定缓存范围为[-128,256];那么jvm配置参数为:
-XX:AutoBoxCacheMax=256 复制代码
此时,我们第一段代码和第二段代码返回结果都是true,因为只要是在[-128,256]范围内的数值全都从IntegerCache缓存中取值,不会去创建新的对象。
注意:我们设置的最大值不可小于127,否则不生效!
比如我们通过jvm配置参数把最大值设置为100:
-XX:AutoBoxCacheMax=100 复制代码
不好意思,你再去执行前面的两段代码,结果依然是第一段代码输出false
,第二段代码输出true
。这就说明我们设置的最大值不能比默认的最大值还小。