C# List<double> 的大小与 double[] 大小的比较

5
所以我刚刚在测试微软的CLR Profiler,并且我写了一个小程序,创建了一个包含1,000,000个double的列表。我检查了堆栈,结果发现List<>大小约为124KB(我不记得确切数字,但大约是这样)。这真的让我震惊,如果它有1百万个double,怎么可能只有124KB呢?无论如何,之后我决定检查一个double[1000000]。出乎意料的是(好吧其实这是我预期的List<>), 数组大小为7.6MB。巨大的差异!

它们为什么不同?List<>是如何管理其元素,以使其(极其)内存高效?我的意思是,不像另外的7.5 MB在其他地方,因为在我创建了1百万个double之后,应用程序的大小增加了约3或4KB。


1
你所说的“应用程序大小大约增加了3或4 KB”的意思是什么?数组是动态分配的,所以最终的可执行文件并不重要。相反,请查看任务管理器中的“内存使用”参数。 - Dewfy
2个回答

18

List<T> 使用数组来存储值/引用,所以我怀疑除了 List<T> 添加的一些开销外,大小不会有任何差异。

给定以下代码

var size = 1000000;
var numbers = new List<double>(size);
for (int i = 0; i < size; i++) {
   numbers.Add(0d);
}

对于相关对象,堆看起来像这样

0:000> !dumpheap -type Generic.List  
 Address       MT     Size
01eb29a4 662ed948       24     
total 1 objects
Statistics:
      MT    Count    TotalSize Class Name
662ed948        1           24 System.Collections.Generic.List`1[[System.Double,  mscorlib]]
Total 1 objects

0:000> !objsize 01eb29a4    <=== Get the size of List<Double>
sizeof(01eb29a4) =      8000036 (    0x7a1224) bytes     (System.Collections.Generic.List`1[[System.Double, mscorlib]])

0:000> !do 01eb29a4 
Name: System.Collections.Generic.List`1[[System.Double, mscorlib]]
MethodTable: 662ed948
EEClass: 65ad84f8
Size: 24(0x18) bytes
 (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
65cd1d28  40009d8        4      System.Double[]  0 instance 02eb3250 _items    <=== The array holding the data
65ccaaf0  40009d9        c         System.Int32  1 instance  1000000 _size
65ccaaf0  40009da       10         System.Int32  1 instance  1000000 _version
65cc84c0  40009db        8        System.Object  0 instance 00000000 _syncRoot
65cd1d28  40009dc        0      System.Double[]  0   shared   static _emptyArray
    >> Domain:Value dynamic statics NYI
 00505438:NotInit  <<

0:000> !objsize 02eb3250 <=== Get the size of the array holding the data
sizeof(02eb3250) =      8000012 (    0x7a120c) bytes (System.Double[])

因此,List<double>占用了8,000,036字节,而底层数组占用了8,000,012字节。这与引用类型(Array)通常的12字节开销和1,000,000个double所需的8字节相吻合。除此之外,List<T>还为上述字段添加了额外的24字节开销。

结论:我没有看到任何证据表明对于相同数量的元素,List<double>double[]占用更少的空间。


没错,我已经三次检查过了,大小几乎相同,实际上List<>稍微大一些。我不知道第一次可能出了什么问题,我在使用CLR分析器运行应用程序时确实遇到了一些麻烦,所以当我运行100万个双精度代码时,可能是在查看应用程序的不同实例。感谢您纠正了这个问题。 - Carlo
Carlo,我学会了质疑所有分析/剖析工具的结果,并且总是要得到“第二意见”,即使结果符合我的期望。往往情况下这只是无法解释的,但有时候“奇怪”背后隐藏着更深刻的理解。 - peterchen

1
请注意,列表是动态增长的,通常在达到内部缓冲区大小时会将其大小加倍。因此,新列表最初可能会有类似于4个元素数组的东西,当您添加了前4个元素后,第5个元素将导致内部重新分配,将缓冲区加倍为(4 * 2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接