浅析 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);
    
        }
    
    }
    

    运行结果:

    Integer Demo 运行结果

    可以看出超出缓存范围后用 == 进行比较会出现 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 欢迎大家来访(#.#)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值