java - 整数数组是如何在 JVM 内部存储的?

标签 java jvm

Java 中的整数数组在内存中存储为 32 位值 block 。 Integer 对象数组是如何存储的?即

int[] vs. Integer[]

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

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

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

最佳答案

据我所知,没有 VM 会像存储 int[] 数组那样存储 Integer[] 数组,原因如下:

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

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

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

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

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

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

关于java - 整数数组是如何在 JVM 内部存储的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76549/

相关文章:

java - 从不同 JVM 中的 Java 桌面应用程序中执行 Java main 方法

java - 在 jar 启动时预加载 java 类/库?

memory - 如何在 Docker 中监控 java 应用程序的内存使用情况

JAVA:循环内定义的引用

java - 将列表 A 中的元素添加到列表 B 的元素中,其中特殊成员相等

java - 如何将新项目导入perforce

java - Spring表单提交以删除集合项

java - 如何保持每次运行的值(value)不变?

java - Windows Defender 阻止在 Java 中获取指针位置

java - 为什么 JVM 总是以 FULL GC 开始?