一尘不染

初始化列表的集合大小是否值得 它的大小是否合理已知?

c#

List<T>如果合理地知道a的集合大小,是否值得初始化?

编辑: 在阅读了第一个答案之后,进一步深化了这个问题,这个问题实际上可以归结为默认容量是什么,以及如何执行增长操作,它会使容量翻倍吗?


阅读 225

收藏
2020-05-19

共1个答案

一尘不染

是的,当您List<T>变大时,它变得很重要。确切的数字取决于元素类型和计算机体系结构,让我们选择32位计算机上的引用类型列表。每个元素将在内部数组中占用4个字节。该列表将以“容量”
0和一个空数组开始。第一次Add()调用将容量增加到4,将内部数组重新分配给16个字节。四个Add()呼叫之后,阵列已满,需要再次重新分配。它的大小增加了一倍,容量增加到8,数组大小增加到32个字节。前一个数组是垃圾。

必要时重复此操作,内部数组的多个副本将变为垃圾。

当数组增加到65,536字节(16,384个元素)时,会发生一些特殊的情况。下一个Add()将大小再次加倍至131,072字节。这是超出“大对象”阈值(85,000字节)的内存分配。现在不再从第0代堆上进行分配,而是从大对象堆中进行分配。

LOH上的对象经过特殊处理。它们只是在第2代收集期间收集的垃圾。而且堆没有被压缩,移动这么大的块需要太多时间。

根据需要重复此操作,几个LOH对象将变成垃圾。它们可能会占用相当长的时间,第二代收集并不经常发生。另一个问题是这些大块倾向于使虚拟内存地址空间碎片化。

这不会无休止地重复,List类迟早需要重新分配数组,并且它已经变得很大,以至于虚拟内存地址空间中没有一个空洞可以容纳该数组。您的程序将使用OutOfMemoryException轰炸。通常在所有可用虚拟内存被消耗之前。

简而言之,通过尽早设置容量,在开始填充列表之前,您可以预先保留较大的内部阵列。您不会在大对象堆中得到所有那些笨拙的已释放块,并避免碎片化。实际上,您将能够在列表中存储更多的对象,并且由于几乎没有垃圾,因此程序运行得更精简。仅当您很好地知道列表有多大时才执行此操作,而使用永远不会填充的大容量是浪费。

2020-05-19