使用 Integer 包装器 class 创建了多少个对象?

How many objects are created by using the Integer wrapper class?

Integer i = 3; 
i = i + 1; 
Integer j = i; 
j = i + j; 

上面示例代码中的语句创建了多少个对象?为什么?有没有 IDE 我们可以看到创建了多少对象(也许在调试模式下)?

大家可以调试Integer.valueOf(int i)方法自行查找。 此方法由编译器的自动装箱过程调用。

令人惊讶的是,答案是零。

从-128到+127的所有Integer都是JVM预先计算的。

您的代码创建了对这些现有对象的引用

严格正确的答案是创建的 Integer 个对象的数量 不确定 。它可能介于 0 和 3 之间,或者 2561 甚至更多2,具体取决于

  • Java平台3,
  • 这是否是第一次执行此代码,并且
  • (可能)其他依赖 int 值装箱的代码是否在它之前运行4.

-128 到 127 的 Integer 值并不严格要求预先计算。事实上,指定 Boxing 转换的 JLS 5.1.7 是这样说的:

If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1) ... then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

需要注意两点:

  • 仅 JLS 需要 >>文字<<。
  • JLS 不强制渴望 缓存值。惰性缓存也满足了 JLS 的行为要求。

甚至 Integer.valueof(int) 的 javadoc 也没有指定 急切地缓存结果。

如果我们从 Java 6 到 8 检查 java.lang.Integer 的 Java SE 源代码,很明显当前的 Java SE 实现策略是预先计算价值。但是,由于各种原因(见上文),这仍然不足以让我们对 "how many objects" 问题给出明确的答案。


1 - 如果上述代码的执行触发 class 初始化 Integer 在缓存被急切初始化的 Java 版本中,则可能是 256在 class 初始化期间。

2 - 如果缓存大于 JVM 规范要求,它可能会更多。在 Java.

的某些版本中,可以通过 JVM 选项增加缓存大小

3 - 除了平台实现装箱的一般方法外,编译器可以发现部分或全部计算可以在编译时完成或完全优化。

4 - 这样的代码可能会触发整数缓存的延迟或急切初始化。

首先:您正在寻找的答案是 0,正如其他人已经提到的那样。

但让我们更深入一点。正如斯蒂芬提到的,这取决于你执行它的时间。因为缓存实际上是惰性初始化的。

如果你查看 java.lang.Integer.IntegerCache 的文档:

The cache is initialized on first usage.

这意味着如果这是您第一次调用您实际创建的任何 Integer:

  • 256 个整数对象(或更多:见下文)
  • 1 个用于存储整数的数组对象
  • 让我们忽略存储 Class(和方法/字段)所需的对象。它们无论如何都存储在元空间中。

从第二次调用它们开始,您创建了 0 个对象。


一旦你把数字调高一点,事情就会变得更有趣。例如。通过以下示例:

Integer i = 1500; 

这里的有效选项是:0、1 或 1629 到 2147483776 之间的任何数字(这次只计算创建的整数值。 为什么? Integer-Cache定义的下一句给出了答案:

The size of the cache may be controlled by the -XX:AutoBoxCacheMax= option.

所以你实际上可以改变实现的缓存的大小。

这意味着您可以到达上面的行:

  • 1: 如果您的缓存小于 1500,则为新对象
  • 0: new Objects 如果你的缓存之前已经初始化并且包含 1500
  • 1629: new (Integer) - 如果您的缓存设置为恰好 1500 且尚未初始化的对象。然后将创建从 -128 到 1500 的整数值。
  • 就像上面的句子一样,你在这里达到了任意数量的整数对象:Integer.MAX_VALUE + 129,也就是提到的:2147483776。

请记住:这仅在 Oracle / Open 上得到保证JDK(我检查了版本 7 和 8)

如您所见,要获得完全正确的答案并不容易。但是只要说0就会让人开心


PS:使用menthoned参数可以使下面的语句为真:Integer.valueOf(1500) == 1500

编译器将 Integer 对象拆箱到 int 中,通过调用 intValue() on them, and it calls Integer.valueOfint 结果分配给 [=] 进行算术计算11=] 变量,所以你的例子相当于:

Integer i = Integer.valueOf(3);
i = Integer.valueOf(i.intValue() + 1);
Integer j = i;
j = Integer.valueOf(i.intValue() + j.intValue());

赋值j = i;是完全正常的对象引用赋值,它不创建新对象。它不进行装箱或拆箱,也不需要,因为 Integer 对象是不可变的。

允许valueOf方法缓存对象,并且return每次为特定数字缓存相同的实例。 需要 缓存 −128 到 +127 的整数。对于你的起始数i = 3,所有的数都是小的,保证缓存,所以需要创建的对象数是0。严格来说,valueOf 允许延迟缓存实例而不是让它们全部预先生成,因此该示例可能仍会在第一次创建对象,但如果代码在程序中重复 运行每次 平均 接近 0 时创建的对象数。

如果您从一个较大的数字开始,其实例将不会被缓存(例如,i = 300)怎么办?那么每次valueOf调用必须创建一个新的Integer对象,每次创建的对象总数为3.

(,也许它仍然是零,也许它是数百万。请记住,出于性能或实现原因,允许编译器和虚拟机重写代码,只要其行为没有其他改变。所以如果你不使用结果,它可以完全删除上面的代码。或者如果你尝试打印j,它可以意识到j 在上面的代码片段之后总是以相同的常量值结束,因此在编译时进行所有算术运算,并打印一个常量值。在幕后完成的实际工作量 运行 你的代码始终是实现细节。)