专栏首页攻城狮的那点事你真的理解 Integer 的缓存问题吗?

你真的理解 Integer 的缓存问题吗?

在开始之前,我们先看看下面给出的这个例子,问输出的结果是多少:

public class IntTest {
    public static void main(String[] args) { 
        Integer a = 100, b = 100, c = 150, d = 150; 
        System.out.println(a == b);
        System.out.println(c == d);
     }
}

很多小伙伴可能非常犹豫,有一些经验的同学可以回答出"标准"答案。

问原因则随口就说”Integer缓存了-128到127之间的整数对象“,为什么会缓存?还有其他答案?可能就不知道了。

what???

难道这不是标准答案?还想咋地?

运行

想知道答案很容易,直接运行,结果如下:

源码法

直接看源码, 我们知道声明整数时,会通过 java.lang.Integer#valueOf(int) 构造(不信可以断点)。

/** 
 * 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);
}

通过源码和注释可以看到,如果是-128到127之间的整数,则会使用整数缓存对象,否则就new一个整形对象。

因此第一个是true,第二个是false。

反汇编

前面讲到了,用到了 再问一个问题 为什么调用了 java.lang.Integer#valueOf(int) ?

我们直接反汇编:javap -c IntTest

Compiled from "IntTest.java"
public class com.chujianyun.common.int_test.IntTest {
  public com.chujianyun.common.int_test.IntTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."&lt;init&gt;":()V
       4: return
 
  public static void main(java.lang.String[]);
    Code:
       0: bipush        100
       2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       5: astore_1
       6: bipush        100
       8: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      11: astore_2
      12: sipush        150
      15: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      18: astore_3
      19: sipush        150
      22: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      25: astore        4
      27: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      30: aload_1
      31: aload_2
      32: if_acmpne     39
      35: iconst_1
      36: goto          40
      39: iconst_0
      40: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
      43: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      46: aload_3
      47: aload         4
      49: if_acmpne     56
      52: iconst_1
      53: goto          57
      56: iconst_0
      57: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
      60: return
}

很明显四个Integer对象的构造使用了java/lang/Integer.valueOf函数。

那么除了上面的回答还有哪些更完善的回答呢?

我们继续看 java.lang.Integer.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=&lt;size&gt;} 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 {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
 
// 省略
}

可以看到可以通过设置虚拟机参数:XX:AutoBoxCacheMax=<size>或 -Djava.lang.Integer.IntegerCache.high=<high>

来设置缓存范围的最大值(包含)。

本文分享自微信公众号 - 攻城狮的那点事(gh_e40249fc5212)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spark2.3.0 共享变量

    通常情况下,传递给 Spark 操作(例如 map 或 reduce)的函数是在远程集群节点上执行的,函数中使用的变量,在多个节点上执行时是同一变量的多个副本。...

    smartsi
  • leetCode求两数之和?

    问题描述:项目中发现,自定义切面注解在 Controller 层正常工作,在 Service 层却无法正常工作。为了便于分析,去掉代码中的业务逻辑,只留下场景。

    技术从心
  • 2019 年的软件趋势

    解读:又到了一年总结和展望的时候了,看看 Pivotal(Spring)公司带来的2019年软件趋势关键词。

    技术从心
  • Spark 如何使用DataSets

    开发人员一直非常喜欢Apache Spark,它提供简单但功能强大的API,这些特性的组合使得用最少的代码就可以进行复杂的分析。我们通过引入 DataFrame...

    smartsi
  • 40个Java多线程问题总结

    一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用...

    技术从心
  • Spark SparkSession:一个新的入口

    在 Spark 1.x 中,使用 HiveContext 作为 DataFrame API 的入口显得并不直观。在 Spark 2.0 引入 SparkSess...

    smartsi
  • Protocol Buffer使用转换工具将proto文件转换成Java文件流程及使用

    Client与Server的网络通信协议传输使用google protobuf,服务器端使用的是Java

    SoullessCoder
  • Hive 正则序列化器RegexSerDe

    RegexSerDe 可以从 Hive 两个jar文件的类中获取,hive-serde-<version>.jar中的 org.apache.hadoop.hi...

    smartsi
  • Flink1.4 生成时间戳与Watermarks

    本节适用于在事件时间上运行的程序。有关事件时间,处理时间和提取时间的介绍,请参阅Flink1.4 事件时间与处理时间。

    smartsi
  • Android使用(TabLayout+ViewPager+fragment)与(FragmentTabHost+ViewPager+Fragment)实现底部状态栏切换

    Android开发过程中,特别是新开的项目,底部状态栏的切换使用的频率非常的高,主要的实现方式有: (1)、TabLayout + Fragment (2)...

    SoullessCoder

扫码关注云+社区

领取腾讯云代金券