代码之家  ›  专栏  ›  技术社区  ›  Yair Halberstadt

在.net中分配新数组的巨大成本

  •  2
  • Yair Halberstadt  · 技术社区  · 7 年前

    在.Net中分配数组的最大时间复杂性是什么?

    我猜想,如果数组足够小,可以放在临时段上,那么它应该是O(1),但是随着n变大,找到足够的内存会变得更加困难,因此它可能会改变。

    此外,大型对象堆可能是碎片化的,因此如果n足够大,使数组适合LOH,那么它可能不是O(1)。

    2 回复  |  直到 7 年前
        1
  •  3
  •   Konrad Kokosa    7 年前

    正如大多数人可能知道的那样,一个新阵列将被分配到两个不同的堆中,具体取决于其大小(大小阈值为85000字节):

    • 小对象堆-这里的分配发生在所谓的 分配上下文 这是内存的预调零区域,位于一个短暂段内。这里可能发生两种情况:
      • 在当前分配上下文中,有足够的空间用于新数组-在这种情况下,我们可以将其视为仅返回数组地址的O(1)操作(并为下一个对象缓冲指针)
      • 没有足够的空间-分配上下文将尝试通过 分配量 (通常是8kB左右)如果可能的话(比如它位于短暂段的末尾)。这里我们讨论了将这些8kB归零的成本,因此它要大得多。更糟糕的是,分配上下文可能无法放大,因为它可能位于已分配的对象之间。在这种情况下,将创建一个新的分配上下文——在空闲列表的帮助下,在临时段的某个地方,以利用碎片。在这种情况下,代价更大——遍历空闲列表以找到合适的位置,然后将其归零。不过,成本并不直接取决于数组大小,它是“常量”,因此我们可以像前面一样将其视为O(1)。

    在LOH分配的情况下,应注意额外隐藏的“成本”-此类分配不会在背景地面军事系统的某些部分发生(因为两者都在免费列表上运行)。因此,如果碰巧您有很多长的背景GCs,LOH分配将暂停,等待GCs结束。这显然会给线程带来不必要的延迟。

        2
  •  2
  •   Thomas Weller    7 年前

    临时段(SOH;小对象堆)中的对象分配在该段上最后一个已知对象之后。它应该只是指向那里的一个指针。

    不考虑中间的“空”空间,因为没有空空间。即使对象不再有引用,它也将一直存在,直到它被垃圾回收。然后,SOH将被压缩,因此同样没有可用空间。

    需要做什么?

    • 也许可以从内核获得新内存。若有,记忆如何? was already zeroed
    • 如果新内存在RAM中不可用,请先将某些内容交换到磁盘。这部分可能会变得非常昂贵,但不可预测。
    • 如果.NET中已有可用内存,则可能需要将其初始化为零。但实施 memset() is optimized (例如使用 rep stos )

    通常,我不会考虑内存的分配问题,除非您已经使用了一个关于内存吞吐量问题的分析器(如dotMead)。相信Donald Knuth:“过早优化是万恶之源”。