浅析 Java 的 Integer 缓存机制
Java 的 Integer 缓存机制简单分析
Java 开发手册中的建议
在 Java 开发手册中的 OOP 规约第七条中是这么说的:
【强制】 所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在 -128 至 127 范围内的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用 == 进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
- 此时可以思考:
- 为何 Integer 会缓存 -128 到 127 之间的赋值?
问题分析
-
代码 Demo
public class IntegerDemo_01 { public static void main(String[] args) { Integer a = 100; Integer b = 100; Integer c = 129; Integer d = 129; System.out.println(a == b); System.out.println(c == d); } }
运行结果:
可以看出超出缓存范围后用 == 进行比较会出现 false 的情况。
因为超出缓存范围后 Integer 内部会 new 一个这个值的实例返回,所以使用 == 判断相等会出现 false 的情况。
-
通过源码进行分析
-
通过打断点的方式,可知道当使用类似 Integer a = 100; 这种形式声明 Integer 变量时,会通过 Integer 中的 valueOf(int) 方法来构造 Integer 对象。
-
那么,可以先看这个方法的源码:
/** * 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 */ public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
通过查看源码,可发现:
当 i 的值在 IntegerCache 的最小值和最大值之间时,会从 IntegerCache 中的 cache 数组中直接提出相对应的值。
否则会通过构造器的方式 new 一个 Integer 对象返回。
再阅读源码的注释:
①This method will always cache values in the range -128 to 127
从这段注释可以知道最小值是 -128,最大值是 127。
②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.
从这段注释可以知道如果不需要新建新的 Integer 实例,则通常应优先使用此方法,因为通过缓存常用的值,会更省空间以及速度会更快。
- 所以,如果想减少内存占用,提高运行效率,可以将常用的对象提前缓存,需要的时候再直接从缓存中拿出来使用。
- 那么,此时是否可以更改 Integer 的缓存区间为更大的区间呢,接下来再进入 IntegerCache 中查看源码。
-
IntegerCache 源码:
/** * 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() {} }
通过这段源码,可以发现最小值固定为 -128,最大值却没有明确指定。
通过注释:
The size of the cache may be controlled by the {@code -XX:AutoBoxCacheMax=} 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.
可以知道缓存的最大值是可以通过虚拟机参数 -XX:AutoBoxCacheMax= 或 java.lang.Integer.IntegerCache.high 来设置。
如果不自行设置则为 127。
-
所以如果有需求,可以通过修改这两个参数之一,使得缓存的区间更大,这样上面的代码 Demo 就可以让结果都为 true 了。
-
再通过这段注释:
-
Cache to support the object identity semantics of autoboxing for values between -128 and 127 (inclusive) as required by JLS.
-
可以知道缓存这个范围的原因:缓存支持 JLS 要求的 -128 到 127 之间的自动装箱过程。也就是说这个范围是 JLS 规定的。
-
JLS 的要求
-
在Boxing Conversion部分的 Java 语言规范 (JLS) 的规定:
如果一个变量 p 的值是:
-128 至 127 之间的整数,
true 和 false 的布尔值,
‘\u0000’ 至 ‘\u007f’ 之间的字符
中时,将 p 包装成 a 和 b 两个对象时,可以直接使用 a == b 判断 a 和 b 的值是否相等。
-
-
-
-
小结
- 为了避免出现问题,在比较 Integer 时还是按照手册的建议使用 equals 方法来比较为好。
如有写的不足的,请见谅,请大家多多指教。
我的个人博客网站: www.xilikeli.cn 欢迎大家来访(#.#)