5

Java 中的整数数组作为 32 位值块存储在内存中。如何存储 Integer 对象数组?IE

int[] vs. Integer[]

我想 Integer 数组中的每个元素都是对 Integer 对象的引用,并且 Integer 对象具有对象存储开销,就像任何其他对象一样。

然而,我希望 JVM 在后台做一些神奇的聪明,因为整数是不可变的,并且像整数数组一样存储它。

我的希望是不是太天真了?在每最后一盎司的性能都很重要的应用程序中,整数数组是否比 int 数组慢得多?

4

5 回答 5

12

我所知道的任何 VM 都不会像 int[] 数组那样存储 Integer[] 数组,原因如下:

  1. 数组中可以有null Integer 对象,并且在 int 数组中没有剩余用于指示这一点的位。但是,VM 可以将每个阵列槽的 1 位信息存储在一个隐藏的位阵列中。
  2. 您可以在整数数组的元素中进行同步。作为第一点,这更难克服,因为您必须为每个数组插槽存储一个监视器对象。
  3. Integer[] 的元素可以进行身份​​比较。例如,您可以通过new创建两个值为 1 的 Integer 对象,并将它们存储在不同的数组槽中,然后您可以检索它们并通过 == 进行比较。这一定会导致错误,因此您必须将此信息存储在某处。或者,您在某处保留对 Integer 对象之一的引用并将其用于比较,并且您必须确保 == 比较之一为假,一个为真。这意味着对于优化的Integer 数组,对象标识的整个概念很难处理。
  4. 您可以将 Integer[] 强制转换为例如 Object[] 并将其传递给只需要 Object[] 的方法。这意味着所有处理 Object[] 的代码现在也必须能够处理特殊的 Integer[] 对象,这使得它变得更慢和更大。

考虑到所有这些,可能有可能制作一个特殊的 Integer[] 与幼稚的实现相比节省一些空间,但额外的复杂性可能会影响许多其他代码,最终使其变慢。

使用 Integer[] 而不是 int[] 的开销在空间和时间上可能会很大。在典型的 32 位 VM 上,一个 Integer 对象将消耗 16 个字节(8 个字节用于对象标头,4 个字节用于负载,4 个额外字节用于对齐),而 Integer[] 使用与 int[] 一样多的空间。在 64 位 VM(使用 64 位指针,并非总是如此)中,整数对象将消耗 24 个字节(16 个用于标头,4 个用于有效负载,4 个用于对齐)。此外,Integer[] 中的插槽将使用 8 个字节而不是 int[] 中的 4 个字节。这意味着您可以预期每个插槽的开销为16 到 28个字节,与普通 int 数组相比,这是4 到 7 倍。

性能开销也很重要,主要有两个原因:

  1. 由于您使用了更多内存,因此您对内存子系统施加了更大的压力,从而在 Integer[] 的情况下更有可能发生缓存未命中。例如,如果您以线性方式遍历 int[] 的内容,则缓存将在您需要它们时已经获取大部分条目(因为布局也是线性的)。但是在 Integer 数组的情况下,Integer 对象本身可能会随机分散在堆中,使得缓存很难猜测下一个内存引用将指向哪里。
  2. 由于使用了额外的内存,垃圾收集必须做更多的工作,因为它必须单独扫描和移动每个 Integer 对象,而在 int[] 的情况下,它只是一个对象,对象的内容不会必须被扫描(它们不包含对其他对象的引用)。

总而言之,在性能关键的工作中使用 int[] 将比在当前 VM 中使用 Integer 数组更快且内存效率更高,并且在不久的将来这不太可能发生太大变化。

于 2008-09-17T07:12:47.117 回答
3

John Rose在 JVM 中使用fixnums来解决这个问题。

于 2008-09-17T05:15:38.600 回答
1

我认为你的希望太天真了。具体来说,它需要处理 Integer 可能为 null 而 int 不能为 null 的问题。仅此一项就足以存储对象指针。

也就是说,实际的对象指针将指向一个不可变的 int 实例,特别是对于整数的选择子集。

于 2008-09-16T20:25:41.883 回答
0

它不会慢很多,但是因为 Integer[] 必须接受“null”作为条目而 int[] 不必接受,所以即使 Integer[] 由整数 []。

因此,如果每一盎司的性能都很重要,用户 int[]

于 2008-09-16T20:26:18.607 回答
0

Integer 可以为 null 而 int 不能为空的原因是,Integer 是一个成熟的 Java 对象,其中包含所有开销。这是有价值的,因为你可以写

Integer foo = new Integer();
foo = null; 

这很适合说 foo 将具有价值,但还没有。

另一个区别是不int执行溢出计算。例如,

int bar = Integer.MAX_VALUE;
bar++;

将愉快地增加 bar 并且你最终得到一个非常负数,这可能不是你最初想要的。

foo = Integer.MAX_VALUE;
foo++;

会抱怨,我认为这是更好的行为。

最后一点是,作为 Java 对象的 Integer 带有对象的空间开销。我认为其他人可能需要在这里插话,但我相信每个对象都会消耗 12 个字节的开销,然后是数据存储本身的空间。如果您追求性能和空间,我想知道 Integer 是否是正确的解决方案。

于 2008-09-16T20:35:16.637 回答