问题 分配数组需要多长时间(用Java)


关于数组分配的一般问题,主要是在Java中,但我认为它与所有编程语言相关:

为大小为n的数组[以O(n)为单位)分配内存需要多长时间?我可以想象一个实现,其中内存分配在恒定时间内发生:如果你有大量的空内存,你可以创建一个指向新数组的第一个和最后一个索引的指针,但是通常如何分配内存? (另外,至少在Java中,如果初始化整数数组,则数组中的所有值最初都设置为0;这是否意味着数组中的每个索引都单独设置为等于0,这将操作O(n)?)

谢谢。


7661
2017-08-11 19:36


起源

您是否尝试过自己的基准测试? - devnull
虽然是开放式的,我认为这个问题会更好地问到程序员。 - gparyani
@devnull他问的是渐近效率而不是时间效率。不知道你怎么能对那个进行基准测试...... - Steve P.
@devnull就此而言,assylias使用基准测试的回答确实提供了一些很好的见解。 - Steve P.
看看基准测试如何随JDK版本变化也很有趣。 - devnull


答案:


我跑了一个 微基准 在hotspot上 - 发布JIT编译,分配一个数组(在i7上)需要:

  • 对于大小为1的数组,大约10 ns
  • 对于10,000个大小的数组,大约400 ns
  • 对于大小为1,000,000的阵列,大约300,000 ns

所以回答你的问题, 根据经验,它似乎是热点上的O(n)

详细结果:

Benchmark                              Mode Thr    Cnt  Sec         Mean   Mean error    Units
c.a.p.ArrayVsList.createArray1         avgt   1      5    2       12.293        0.867  nsec/op
c.a.p.ArrayVsList.createArray10000     avgt   1      5    2      428.369        9.997  nsec/op
c.a.p.ArrayVsList.createArray1M        avgt   1      5    2   342972.975     7253.989  nsec/op
  • 在java中创建一个对象,如果你的堆足够大,几乎是免费的(它大致包括偏移一个指针)
  • 但是JVM似乎急切地将所有项目初始化 null (或者在创建数组时为0)
  • 其他JVM可能会执行更长时间的初始化

9
2017-08-11 19:41



看看更大阵列的分配速度会很有趣。我原来的问题是关于O(n),但我认为摊销的复杂性应该与实际运行时间有些相关。 - tmldwn
@tmldwn见更新 - assylias
+1很棒的答案,谢谢你的洞察力。 - Steve P.
根据经验数据,这似乎是一个合理的解释,而lejlot的字节代码示例似乎证实了这一点。谢谢。 - tmldwn
+1照常,彻底和科学。为大小为零的数组添加时序可能会很有趣,这将根据经验显示设置数组的开销(在为元素引用分配内存之前) - Bohemian♦


你已经自己回答了这个问题 O(n) 在Java中,因为元素被初始化,而有些语言就是这种操作 O(1) 因为没有初始化。

只是为了确定

public class ArrayTest {

  public static void main(String[] args) {
     int[] var = new int[5];
  }

}

产生

public class ArrayTest extends java.lang.Object{
public ArrayTest();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   iconst_5
   1:   newarray int
   3:   astore_1
   4:   return

}

和文档说 newarray

一个新数组,其组件的类型为atype,长度为count   从垃圾收集堆中分配。引用arrayref到   这个新的数组对象被推入操作数堆栈。  的   新数组的元素是 初始化为默认初始值   对于数组的类型(§2.5.1)。


3
2017-08-11 19:40



不一定 - 唯一的保证是当你第一次访问一个数组项时,它的值必须为0 - 没有什么说它必须在初始化时完成。 - assylias
该字节码如何帮助证明您的观点? - arshajii
它显示了内部功能 newarray 被调用,其中记录了有关元素初始化的信息: docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html - lejlot
规范措辞没有按字面意思实施。字节码就像发生的事情一样讲述Java代码。 - Esailija


AFAIK,阵列分配总是O(n),直到你可以拥有的最大尺寸。这是因为将存储器归零的成本也是O(n)。系统可以采取一些技巧来加快速度。即它不必将每个字节清零,它可以使用一些虚拟内存技巧将整个页面清零,但即便如此,这也是O(n)。


3
2017-08-11 19:55