大型小对象的缓慢初始化

时间:2015-12-01 21:21:55

标签: java arrays performance

我今天偶然发现了这个案子,我想知道这个巨大差异背后的原因是什么。

第一个版本初始化一个5k x 5k的raw int数组:

public void initializeRaw() {
  int size = 5000;
  int[][] a = new int[size][size];
  for (int i = 0; i < size; i++)
    for (int j = 0; j < size; j++)
      a[i][j] = -1;
}

我的机器大约需要300毫秒。 另一方面,使用简单的2-int结构初始化相同的数组:

public class Struct { public int x; public int y; }

public void initializeStruct() {
  int size = 5000;
  Struct[][] a = new Struct[size][size];
  for (int i = 0; i < size; i++)
    for (int j = 0; j < size; j++)
      a[i][j] = new Struct();
}

需要超过15000毫秒。

我希望它有点慢,毕竟有更多的内存要分配(如果我没有弄错的话,10个字节而不是4个字节),但是我不明白为什么它需要花费50倍的时间。 / p>

有人可以解释一下吗?也许只有更好的方法在Java中进行这种初始化?

编辑:对于某些比较 - 使用Integer而不是int / Struct的相同代码工作700ms - 只慢两倍。

2 个答案:

答案 0 :(得分:4)

  

我希望它有点慢,毕竟有更多的内存要分配(如果我没有弄错的话,10个字节而不是4个字节),但我不明白为什么它需要花费50倍的时间。

不,比这更糟糕。在第一种情况下,您将创建5001个对象。在第二种情况下,您将创建25,005,001个对象。我怀疑,每个Struct对象将占用16到32个字节。 (这将取决于各种JVM细节,但这是一个粗略的猜测。)

第一种情况下你的5001个对象总共需要大约100MB。等效对象(数组)可能总共需要大约200MB,如果你在64位引用的平台上......然后那么还有其他2500万个对象需要分配和初始化。

所以是的,差异很大......

答案 1 :(得分:3)

当您创建5000个int的数组时,您将一次性分配所有这些整数所需的所有空间,作为单个连续元素块。为每个数组元素分配一个int时,您没有分配任何内容。与5000个Struct实例的数组形成对比。您遍历该数组,并为这5000个元素中的每一个元素分配一个Struct实例。分配对象比简单地将int值写入变量需要更长的时间。

你有二维数组的事实在这里没有太大的比较差异,因为它只是意味着你在这两种情况下都会分配5000个数组对象。

如果要对一组Integer对象进行计时,然后将每个元素设置为-1,那么每次都不会分配单独的Integer对象。相反,您正在使用自动装箱,这意味着编译器隐式调用Integer.valueOf(-1),并且该方法每次都从缓存中返回相同的对象。

更新:回过头来解决您的问题,如果我理解正确,您需要将5000x5000 Structs保留在2D数组中,并且您对创建此数组比使用原语需要更长时间感到失望。为了提高性能,您可以创建两个基元数组,每个基元一个,但这会降低代码清晰度。

您还可以创建一个long数组(因为每个long的长度是int的两倍)并使用&amp;和&gt;&gt;运营商获得原始的整数。同样,这会降低代码清晰度,但是你只有一个数组。

但是,您似乎专注于代码的单个部分,即数组的创建。您可能会发现,对每个元素执行的处理会使创建数组所花费的时间过多。分析整个应用程序,看看阵列的创建是否重要。