关于java:聊聊Java-Integer缓存池IntegerCache

36次阅读

共计 6755 个字符,预计需要花费 17 分钟才能阅读完成。

聊聊 Java Integer 缓存池 IntegerCache

目录

  1. 开篇引入经典案例
  2. IntegerCache 介绍
  3. 字节码验证
  4. 论断
  5. 扩大
  6. 扩大论断
  7. 写在结尾

本文由浅入深,全篇难度并不高,可能有些地位对于局部同学是盲区。

焦急的同学能够间接跳到论断地位查看论断。

字节码验证、扩大环节老手浏览可能有些许艰难,能够记录后,期待将来再学习一段时间再看也能够。

引入经典案例

从一个经典的案例开始

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 类的公有零碎属性中。

重点关键字

  1. -128~127
  2. 大小可由 -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。

写在结尾

自己是 程序猿大辉,一个仍在辛勤奋斗的程序猿,心愿你我都能在将来的路上一直学习、一直成长。

同时不要失落高兴,做一个高兴的程序猿。

心愿本文能够给你带来播种。

正文完
 0