聊聊 Java Integer 缓存池 IntegerCache
目录
- 开篇引入经典案例
- IntegerCache 介绍
- 字节码验证
- 论断
- 扩大
- 扩大论断
- 写在结尾
本文由浅入深,全篇难度并不高,可能有些地位对于局部同学是盲区。
焦急的同学能够间接跳到论断地位查看论断。
字节码验证、扩大环节老手浏览可能有些许艰难,能够记录后,期待将来再学习一段时间再看也能够。
引入经典案例
从一个经典的案例开始
public class IntegerCacheExample {public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.out.printf("a==b: %s.\n", a == b);
Integer c = 128;
Integer d = 128;
System.out.printf("c==d: %s.\n", c == d);
}
}
很简略两个输入,想个五秒钟后查看答案。
a==b: true.
c==d: false.
不晓得跟你想的答案是 否一样,咱们上面说一下为什么。
Integer 缓存池 IntegerCache
Integer 缓存池?是跟字符串缓存池相似的货色么?
缓存池的叫法是译文,跟字符串缓存池并不是一个意思,惟一类似的点就是池子里的确存储了一些值。
原文为 IntegerCache,让咱们去java.lang.Integer 源码中一探到底。
本文翻看的源码是 JDK1.8.0-openjdk
大概 780 行左右找到 外部类 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 -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 {
谷歌翻译解释如下:
缓存以反对 JLS 要求的 -128 和 127(含)之间值的主动装箱的对象标识语义。缓存在第一次应用时初始化。缓存的大小能够由 -XX:AutoBoxCacheMax=<size> 选项管制。在 VM 初始化过程中,java.lang.Integer.IntegerCache.high 属性可能会被设置并保留在 sun.misc.VM 类的公有零碎属性中。
重点关键字:
- -128~127
- 大小可由 -XX:AutoBoxCacheMax 调整
能够失去解释缓存生成的范畴是 -128~127,参数能够启动之前配置 JVM 参数 AutoBoxCacheMax 进行批改。
由此咱们晓得了为什么 a == b 是 true,而 c == d 是 false,因为 a 和 b 都是取自缓存池,指向的是同一指针,而 c 和 d 不从缓存池中获得,指向的是各自的内存地址。(如果不太了解什么是 new 对象指向新的内存地址的话,能够参考博主内存篇,找不到的话就是,内存篇可能还没写完,尚未公布)
咱们尝试来验证一下这个论断是否正确
IntegerCache 字节码验证- 此段老手浏览较难,能够繁难浏览,不用深究,将来都会弄懂
先从指针地址上查看一下。运行如下代码
System.identityHashCode(Object x)办法能够返回对象的内存地址,不论该对象的类是否重写了 hashCode()办法。
System.out.printf("a:%s.\n", System.identityHashCode(a));
System.out.printf("b:%s.\n", System.identityHashCode(b));
System.out.printf("c:%s.\n", System.identityHashCode(c));
System.out.printf("d:%s.\n", System.identityHashCode(d));
输入如下:
a:1118140819.
b:1118140819.
c:1975012498.
d:1808253012.
能够看到其中 a,b 指向同一地址,c,d 指向不同地址。接着咱们从字节码的角度看一下 JVM 如何做到的。
为了缩小字节码复杂度,我正文了比照局部的代码
public class IntegerCacheExample {public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
// System.out.printf("a==b: %s.\n", a == b);
Integer c = 128;
Integer d = 128;
// System.out.printf("c==d: %s.\n", c == d);
}
}
生成字节码,在 main 办法这个类的目录下命令行模式执行命令,会生成一个同名的.class 文件
javac IntegerCacheExample.java
查看字节码
javap -c IntegerCacheExample.class
字节码如下:
Compiled from "IntegerCacheExample.java"
public class com.xh.basic.lang.integer.IntegerCacheExample {public com.xh.basic.lang.integer.IntegerCacheExample();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 127
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: bipush 127
8: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11: astore_2
12: sipush 128
15: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
18: astore_3
19: sipush 128
22: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
25: astore 4
27: return
}
是不是看不懂,看不懂不要焦急,我也不是全能看懂,咱们来找一些要害地位解读即可。
public static void main 下的是开始了咱们的次要代码,上方局部能够主动疏忽即可,咱们只关注重点。
在 main 下的 2、8、15、22 行处咱们能看到
// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer
能够猜到 JAVA 在运行时进行了如下优化调整
Integer x = num;
// 优化为
Integer x = Integer.valueOf(num);
咱们随着源码来到 Integr.valueOf 办法内
public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
源代码通知咱们当执行 valueOf 时,先判断 i 是否在 [low, high] 范畴内 ,如果是范畴内,则取自 IntegerCache,不在范畴的则进行 new Integer(i) 操作,所以咱们能够得出结论。
论断
当 Integer x = num 初始化时,如果 num 在 -128~127 范畴内(默认范畴),会从 IntegerCache 中获取值,此时会指向同一指针地址,范畴外时会 new Integer 操作,指向不同指针。
扩大
咱们看到 IntegerCache 正文中提到了 -XX:AutoBoxCacheMax,咱们能够试一下,看有没有成果,更改后是什么扭转。
咱们在 JVM 运行参数上加上 -XX:AutoBoxCacheMax=200
我是用的是 IDEA,局部 idea 没有这个 VM options,咱们在界面地位顺次抉择
Run –> Eidt Configurations –> Modify options –> Add VM options
增加之后就能在界面地位看到了,能够增加运行参数
Apply 后 运行代码 查看成果
AutoBoxCacheMax=200
sun.misc.VM.getSavedProperty(“java.lang.Integer.IntegerCache.high”);
此办法是获取 VM 参数中的 AutoBoxCacheMax 的值
public class IntegerCacheExample {public static void main(String[] args) {System.out.printf("high:%s.\n", sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"));
Integer a = 127;
Integer b = 127;
System.out.printf("a==b: %s.\n", a == b);
Integer c = 128;
Integer d = 128;
System.out.printf("c==d: %s.\n", c == d);
Integer e = 200;
Integer f = 200;
System.out.printf("e==f: %s.\n", e == f);
Integer g = 201;
Integer h = 201;
System.out.printf("g==h: %s.\n", g == h);
System.out.printf("a:%s.\n", System.identityHashCode(a));
System.out.printf("b:%s.\n", System.identityHashCode(b));
System.out.printf("c:%s.\n", System.identityHashCode(c));
System.out.printf("d:%s.\n", System.identityHashCode(d));
System.out.printf("e:%s.\n", System.identityHashCode(e));
System.out.printf("f:%s.\n", System.identityHashCode(f));
System.out.printf("g:%s.\n", System.identityHashCode(g));
System.out.printf("h:%s.\n", System.identityHashCode(h));
}
}
后果输入如下:
high:200.
a==b: true.
c==d: true.
e==f: true.
g==h: false.
a:1118140819.
b:1118140819.
c:1975012498.
d:1975012498.
e:1808253012.
f:1808253012.
g:589431969.
h:1252169911.
能够看出 200 时取自 IntegerCache, 201 则是不同指针的 new Integer 了。配置失效
AutoBoxCacheMax=126
看命名能看出是 Max 值,那如果比 127 小呢,是否无效?咱们测试一下。
public class IntegerCacheExampleMinValue {public static void main(String[] args) {System.out.printf("high:%s.\n", sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"));
Integer a = 127;
Integer b = 127;
System.out.printf("a==b: %s.\n", a == b);
Integer c = 128;
Integer d = 128;
System.out.printf("c==d: %s.\n", c == d);
Integer min126a = 126;
Integer min126b = 126;
System.out.printf("min126a==min126b: %s.\n", min126a == min126b);
System.out.printf("a:%s.\n", System.identityHashCode(a));
System.out.printf("b:%s.\n", System.identityHashCode(b));
System.out.printf("c:%s.\n", System.identityHashCode(c));
System.out.printf("d:%s.\n", System.identityHashCode(d));
System.out.printf("min126a:%s.\n", System.identityHashCode(min126a));
System.out.printf("min126b:%s.\n", System.identityHashCode(min126b));
}
}
输入如下:
high:126.
a==b: true.
c==d: false.
min126a==min126b: true.
a:697960108.
b:697960108.
c:943010986.
d:1807837413.
min126a:2066940133.
min126b:2066940133.
min126a==min126b 没问题,然而为什么 a ==b?此时缓存池的值 high 不是 126 么?那 127 应该是 new 的啊?难道配置没失效,其实还是 127?
其实不是咱们设置的没失效,通过打印 high 的值,咱们发现配置的确是失效了的,但没失效的起因是源码中做了判断。贴一段 IntegerCache 中为缓存池最大值赋值的办法。
String integerCacheHighPropValue =
sun.misc.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.}
}
要害代码
i = Math.max(i, 127);
能够看出,它取 vm 参数 AutoBoxCacheMax 的值与 127 做判断,取最大值,也就是说,如果咱们设置的 AutoBoxCacheMax 参数比 127 小,则不会失效。
到此咱们的 IntegerCahce 篇就完结了,如果有不明确的小伙伴能够文末留言给我,我会一一回复的。
扩大论断
-XX:AutoBoxCacheMax 设置能够批改缓存池的最大范畴,但须要大于 127 能力失效,小于等于 127 时,仍然取的是默认值 127。
写在结尾
自己是 程序猿大辉,一个仍在辛勤奋斗的程序猿,心愿你我都能在将来的路上一直学习、一直成长。
同时不要失落高兴,做一个高兴的程序猿。
心愿本文能够给你带来播种。