Java中包装类型的cache机制

问题引入

由一段代码讲起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
Integer i3 = Integer.valueOf(127);
Integer i4 = new Integer(127);
Integer i5 = 128;
Integer i6 = 128;
System.out.println(i1 == i2);
System.out.println(i1 == i3);
System.out.println(i3 == i4);
System.out.println(i5 == i6);
}
}

想一想输出结果是什么?

1
2
3
4
true
true
false
false

其它还好说,可能对于一点有点迷惑,那就是为什么i1==i2输出为true,而i5==i6输出为false呢?

这就涉及到Java中的Cache机制了。

为了节省内存和提升Integer类性能,从Java5开始,引入了缓存机制,取值在一定范围内的Integer对象会被缓存起来。以后如果要创建对象,就会先判断该数值在不在缓存中,如果在就直接返回缓存中的对象,否则才创建。

Cache机制只有在自动装箱和手动装箱(即调用Integer.valueOf()方法)时才会有效。这也就解释了为什么i1、i2、i3是同一个对象了。而i4是直接创建对象,所以不会用到Cache机制。

那为什么i5!=i6呢?上面所说取值范围内才会缓存Integer对象,难道128不在这个取值范围内?

带着这个问题,我去学习了下源码。


Cache机制如何实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 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 {@code -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
* jdk.internal.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 =
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() {}
}

一步步分析源码。

IntegerCache是一个静态内部类。在类中定义了一个cache数组,不用说,这是用来存放Integer对象的。另外还有一个静态块,在第一次调用这个类的时候,会创建[low,high]范围内的对象将它们存起来。

那怎么知道high的大小呢?

可以看到这行代码

1
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

这行代码的意思是获取jvm相关参数,如果没有这个参数,那么integerCacheHighPropValue自然就是null,既然是null,那么就不会执行if分支。如果有,那么在if分支里,h的值就会被改变为jvm参数。

由此,都解释的通了。

自动装箱与Integer.valueOf的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

Integer i = 1;
会自动调用valueOf方法进行创建对象。

扩展

在代码中看到这行

1
// Maximum array size is Integer.MAX_VALUE

搜了下,了解了一个知识。ArrayList的最大长度为Integer.MAX_VALUE-8,因为要有8位来存储ArrayList的长度。


除了Integer外,还有Byte、Short、Long、Character都有缓存机制,cache[]长度不可改变。

包装类型 范围
Integer -128~127
Byte -128~127
Short -128~127
Long -128~127
Character 0~128

Boolean的取值只有两个,会先创建true、false对象,使用valueOf来创建对象会根据方法的参数来决定返回哪个对象。

1
2
3
4
5
6
7
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

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

-------------本文结束感谢您的阅读-------------
0%