【问题标题】:Why Integer class caching values in the range -128 to 127?为什么整数类缓存值在 -128 到 127 范围内?
【发布时间】:2014-01-20 17:33:30
【问题描述】:

关于我之前的问题 Why do == comparisons with Integer.valueOf(String) give different results for 127 and 128? ,我们知道 Integer class 有一个缓存,用于存储 -128127 之间的值。

只是想知道,为什么介于 -128 和 127 之间?

Integer.valueOf() documentation 声明它缓存频繁请求的值。但是,-128127 之间的值是否经常被真实请求?我认为经常要求的值是非常主观的。
这背后有什么可能的原因吗?

从文档中还指出:..并且可能会缓存此范围之外的其他值。
这是如何实现的?

【问题讨论】:

  • 重新查看文档:Oracle 只是在掩盖他们的问题,以防他们以后决定改变行为。例如,他们可能决定 Java 9 将缓存从 -1024 到 1023。消息是,不要依赖缓存包含或不包含任何特定整数。
  • 我假设您从 0 到 X 的循环次数比从 13476 到 Y 的循环次数要多得多。他们一定已经决定也应该包含负值,并且 -128 -> 127 对于有符号字节是有意义的。
  • 循环不是几乎总是用原始整数完成的——不是装箱的整数吗?缓存不适用。
  • 缓存纯粹是一个性能的东西。只要它不会给您带来性能问题,您就不应该关心缓存的范围。 (在代码中构建对整数缓存的依赖是非常愚蠢的。)
  • @JohnR 它在 Java 语言规范中,请参阅下面的 assylias 答案。

标签: java caching


【解决方案1】:

只是想知道,为什么介于 -128 和 127 之间?

可以缓存更大范围的整数,但至少必须缓存介于 -128 和 127 之间的整数 ,因为这是由 Java Language Specification 强制要求的(强调我的):

如果被装箱的值 p 是真、假、字节或字符,在 \u0000 到 \u007f 范围内,或者 -128 到 127(含)之间的 int 或短数字,则令 r1 和 r2 为 p 的任意两次装箱转换的结果。 r1 == r2 总是如此。

此要求的基本原理在同一段落中进行了解释:

理想情况下,对给定的原始值 p 进行装箱,总是会产生相同的引用。在实践中,使用现有的实现技术这可能是不可行的。上述规则是一种务实的妥协。上面的最后一个子句要求某些公共值总是被装箱到无法区分的对象中。 [...]

这可确保在最常见的情况下,行为将是所需的行为,而不会造成过度的性能损失,尤其是在小型设备上。例如,内存限制较少的实现可能会缓存所有 char 和 short 值,以及 -32K 到 +32K 范围内的 int 和 long 值。


如何缓存此范围之外的其他值?

您可以使用-XX:AutoBoxCacheMax JVM 选项,available Hotspot JVM Options 列表中并未真正记录该选项。但是the comments inside the Integer class around line 590中提到了:

缓存的大小可以由-XX:AutoBoxCacheMax=<size> 选项控制。

请注意,这是特定于实现的,可能在其他 JVM 上可用,也可能不可用。

【讨论】:

  • 这是完整且最佳的答案 - 这个问题将 -128 到 127 范围与“经常请求的值”混淆了,而实际上它们是出于不同的原因。 -128 到 127 被缓存用于装箱。缓存“经常请求的值”以提高性能。
  • @ZacThompson,感谢您指出这一点。我之前的评论不正确。规范中的关键短语是“一个 int ... 介于 -128 和 127(含)之间,然后让 r1 和 r2 成为 p 的任意两次装箱转换的结果。r1 == r2 总是如此。”因此,如果我理解正确,规范要求 Integer.valueOf(X) == Integer.valueOf(X) where -128
  • 这是问题的“为什么”部分的唯一答案,它提供了“它是默认值”以外的其他内容。但是,这个答案并不完整,因为它没有解决问题的“如何”部分。参考其他人在 XX:AutoBoxCacheMax 上的响应并添加有关如何控制 JVM 其他实现上的缓存行为的信息(或指示哪些 JVM 实现具有控制此行为的选项)将使这成为一个完整的答案。
  • “在实践中,使用现有的实现技术可能不可行。”我无法得到这条线。你能解释一下吗?
  • @niiraj874u 当前实现使用驻留在内存中的缓存 - 每个“规范”整数都保存在该缓存中。因此缓存所有整数意味着您可能必须在内存中保存多达 2^32 个整数 (= 15+ GB),这是不合理的,即使在现代台式计算机上也是如此。
【解决方案2】:

-128 到 127 是默认大小。但是 javadoc 还说整数缓存的大小可能-XX:AutoBoxCacheMax=<size> 选项控制。请注意,它只设置高值,低值始终为 -128。此功能是在 1.6 中引入的。

至于为什么 -128 到 127 - 这是字节值范围,很自然地将其用于非常小的缓存。

【讨论】:

  • 我们如何实现-XX:AutoBoxCacheMax=<size>
  • 运行 java -XX:AutoBoxCacheMax=256 ... 你会看到 Integer.valueOf(256) == Integer.valueOf(256)
  • 通过在控制台中运行java -XX:AutoBoxCacheMax=256,我得到了Error:could not create the Java Virtual Machine
  • 试试 java -version 应该是 1.6 或更高,我的 1.7 工作正常
  • 对,这就是为什么 javadoc 说 ..may be controlled... 我的 Java 是 64 位
【解决方案3】:

缓存小整数的原因(如果您要问的话)是许多算法在其计算中使用小整数,因此避免这些值的对象创建开销往往是值得的。

然后问题就变成了要缓存哪些整数。再说一遍,一般来说,使用常数值的频率会随着常数绝对值的增加而减少——每个人都花很多时间使用值 1 或 2 或 10,很少有人非常使用值 109密集;更少的性能取决于获得 722 的整数的速度。Java 选择分配 256 个插槽,跨越有符号字节值的范围。这一决定可能是通过分析当时存在的程序得出的,但也很可能是纯粹武断的决定。这是一个合理的空间投入,可以快速访问(掩码找出值是否在缓存的范围内,然后快速查找表以访问缓存),它肯定会涵盖最常见的情况。

换句话说,我认为你的问题的答案是“它不像你想象的那么主观,但确切的界限在很大程度上是一个经验法则决定......并且实验证据表明它是足够好。”

【讨论】:

    【解决方案4】:

    可以通过系统属性配置可以缓存的最大高整数值,即java.lang.Integer.IntegerCache.high(-XX:AutoBoxCacheMax)。缓存是使用数组实现的。

        private static class IntegerCache {
        static final int high;
        static final Integer cache[];
    
        static {
            final int low = -128;
    
            // high value may be configured by property
            int h = 127;
            if (integerCacheHighPropValue != null) {
                // Use Long.decode here to avoid invoking methods that
                // require Integer's autoboxing cache to be initialized
                int i = Long.decode(integerCacheHighPropValue).intValue();
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - -low);
            }
            high = h;
    
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
    
        private IntegerCache() {}
    }
    

    【讨论】:

      【解决方案5】:

      当您遇到 Integer 类并且总是在 -128 到 127 范围内装箱时,最好将 Integer 对象转换为 int 值,如下所示。

      <Your Integer Object>.intValue()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-02-26
        • 2023-03-31
        • 2021-09-09
        • 2022-01-22
        相关资源
        最近更新 更多