Java中的Integer缓存问题,这么一看,真简单

编码之外

共 3264字,需浏览 7分钟

 · 2021-08-15

先来看一个经典的问题,代码演示如下:


public static void main(String[] args) {       Integer a = 127;       Integer b = 127;
Integer c = 128; Integer d = 128;
System.out.println(a == b); System.out.println(c == d); }


现在请你说出打印输出的结果是多少?



怎么样,答案是否和你想的一样呢?为什么会出现这样定位情况呢?


稍微思考一下,我们应该就能发现,问题肯定是出现在后面的赋值,像这种问题,一定是有没有特殊的情况,存在一个临界值,而127和128绝对和这个临界值有关,这是我们应该有的思考!


接下来我们就一步步的来分析这个问题!

揭开integer的缓存面纱

下面我们通过源码的方式去看下,为什么会存在上述问题,在说明这个问题之前,我想先带大家回顾一些基础知识:


  1. 一般来说,对于基础数据类型来说,“==”号就是用来比较两个数据是否相等,比较的就是字面值,但是对于引用数据类型来说,“==”号用来比较两个数据的对象地址是是否一样,而equals方法则是用来比较两个数据的字面值是否一致
  2. Java中的数据基本分为基础数据类型引用数据类型两大类


ok,接下来我们把注意力放在这段代码上面:


Integer a = 127;
Integer b = 127;
System.out.println(a == b);


接下来我们来打断点查看:



ok,我们在这里加上断点之后,我们debug运行:



此时运行结果如上图,我们点击红色箭头所指的强制进入方法按钮,点击一下回来到这里:



那么到了这里,你就得明白一个问题了,为什么我们从这里到了这里,看图:



明白什么意思了吧,也就是说,你通过Integer xx = xx的形式去声明变量,实际上就是通过Integer的这个valueOf方法来构造Integer对象的,明白了这点之后我们就要注重看看这个valueOf方法了,缓存问题就藏在这里:



在这里我们是不是可以一眼就看到这么一个类“IntegerCache”,是不是很简单,翻译下就是整型缓存,我们注意看这里:



这里是不是就是对我们的赋值做判断,那这个low和high是不是就是代表最大值和最小值,我们查看下,是多少?



可以看到,这里最小值就是直接定义的-128,那这个最大值这里没有给出,我们往下找找,在这里有描述:



这里其实也给出了一个默认值127,但是为什么不像最小值那样直接给出呢?我们看这里有一句描述:



翻译过来的意思就是说,这个最大值可以high可以通过配置改变,并不是一直都是127 ,这个127只是个初始默认值!


ok,那到了这里我们应该清楚,我们没有做什么特殊的配置的话,默认的最大值是127,默认的最小值是-128,再看这里:


如何返回缓存中的值


那这段代码的意思就明白了,也就是说如果我们的赋值大小在-128到127之间的话,那就直接返回缓存中的值,如果不是的话,就通过new Integer的形式创建一个新的对象,这点没什么问题吧,那接下来我们需要分析下这个返回的缓存中的值是如何得出的,也就是这段代码:


IntegerCache.cache[i + (-IntegerCache.low)]


这是啥意思?我们先来看这个cache:



这里是定义了一个Integer的数组cache,那这个cache数组多大呢?



是不是就是根据我们的最小值和最大值来确定的,比如举个简单的例子,我们最小值是1,最大值是5,那就是1-5之间所有数字,是不是一共有5个元素,

那我们再看上面的是不是就是cache = new Integer[(5-1)+1] = new Integer[5],ok,这里明白吧,只不过我们这里的最大值是127,最小值是-128,之所以举例1和5是为了让你更加好理解!


ok,我们现在知道这个cache数组有多大了,那里面都是什么值呢?看这里:



怎么样,看的明白吗?还拿我们的1和5举例子,此时最小值是不是是1,上述代码是不是就是从1开始一直到5,将其填充到cache数组中去,而这里我们要注意数组下标是从0开始的,是不是就是这样:



接下来我们再来看这里的取值:



假如我们输入的i值是3,也就是Integer a = 3,此时我们的cache缓存是范围是1-5,所以这里会直接返回给我们缓存中的值,是不是就是这样:


IntegerCache.cache[3 + (-1)]是不是就是cache[2]那值是不是就是3了,所以直接返回的还是输入的i值3.


咋样这里明白吧,还是那句话,我这里用最小值1和最大值5举例就是方便理解,换成实际的最小值-128和最大值127是一样的道理!

缓存最大值的修改


经过上面的分析,我们看到了这里:



也就是这个最大值是可以通过配置修改的,那如何修改呢?我们可以通过以下两个虚拟机参数来修改:


-XX:AutoBoxCacheMax=<size>-Djava.lang.Integer.IntegerCache.high=<value>


比如我这里修改:



然后我们再运行之前的代码:



是不是都变成了ture了!如果我们不设置的哈,那默认的就是127的值了,也就是缓存-128到127的值了!

为什么缓存这个范围的值


那到了这里,你是否会好奇,为什么缓存-128-127之间的值呢,我们来看这里的描述:



也就是说,缓存这些值来达到服用,是这个JLS的要求,查找相关资料,我们可以得出在JLS的Boxing Conversion部分有这样的描述:



那这个JLS是个什么玩意,竟然还能要求这些?


这玩意就是Java语言规范Java Language Specification,简称JLS,它指定Java编程语言的语法以及其他说明什么是有效Java程序的规则等等,关于这个,大家简单了解即可,说白了,人家是Java语言规范啊,就是Java的执行标准,你得按照这个规范来!


之所以有这样的规范,也是考虑到性能问题,也就是说-128到127的值我们可能用的比较多,所以就没必要每次创建都新建对象去浪费空间,既然经常用,就缓存起来,高效省空间,何乐而不为呢?


ok,到了这里,你应该清楚为什么要缓存-128到127之间的值了吧!

举一反三,看看Short缓存问题


在学习完关于Integer缓存问题,让我们再一起简单看下Short是不是也有相关的缓存,同样的代码:



看代码输出结果:



看到这样的结果,我们已经可以猜到了,这个Short看来也有缓存问题啊,我们同样通过断点调试进入底层源码查看:



果然,这里也是缓存了-128到127之间的值,接着我们看看这个缓存是如何构造的:



可以看到,Short的数组大小要比Integer的来的简单,而数组内容的填充也是通过静态代码块的形式,对了忘了说了,我们在看Short缓存问题的时候想必你也发现了,我们通过Short xx =  xx的形式声明变量的时候,其实也是通过以下方法来构造Short对象的:



可以发现,Short和Integer在缓存问题上也是有很多相似之处的,具体的就不带着大家看了,感兴趣的可以自己研究下!


ok,今天的分享就到这里,希望对你有所收获!

庆哥还写了以下干货,欢迎阅读:

听说公众号要凉凉了?
我们写的Java代码到底是如何运行的?
涨姿势啦!Java程序员装X必备词汇之Mark Word!
朋友圈爆了!!!



庆哥建立了Java学习交流群,感兴趣的可以添加庆哥好友,
我会经常分享学习资源在里面,大家共同进步!


LOVE

点个在看你最好看

浏览 40
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报