List<object> 内存开销

9

我有这样的东西:

List<object> res = new List<object>();
...
while (...)
{
   byte[] val = //get new byte[4]
   res.Add(val); //if this is commented memory usage goes from 2Gb to 180Mb
}

return res;

现在val总是byte[4],大约有37000000个元素。所以我认为res应该在37*10^6 * 4字节左右 = 148 MB,但实际的内存使用量约为2GB。如果我注释掉res.Add(val);,那么内存使用量会在100 MB左右。那么内存去哪了呢?

enter image description here enter image description here

编辑

我尝试使用uint代替byte [4],但结果相同: 在此输入图像描述

编辑2

好的,使用uint代替byte [4] List<uint>()代替List<object>将内存减少到约300 MB。


1
你如何计算内存使用量?任务管理器? - MakePeaceGreatAgain
5
"2 GB for 37 million objects comes down to 54 bytes per boxed value, including the bookkeeping of the runtime (GC) and the overhead of .NET in general, and your program isn't small, as you indicate a footprint of 100 MB when it's doing absolutely nothing. Why do you think the List<T> is relevant? Doesn't an array display the same behavior? What problem are you trying to solve?"这段话的意思是:3700万个对象占用了2GB的内存,每个对象平均包含54字节的空间,其中包括运行时(GC)的记账和.NET框架的开销。而你所说的程序,即使什么也不做,其占用空间也已经达到了100MB。那么您认为使用List<T>有什么关联?难道数组不会表现出同样的行为吗?您试图解决什么问题呢? - CodeCaster
@HimBromBeere 使用VS调试器,查看屏幕截图。 - ren
@CodeCaster 我刚刚尝试使用new object[37000000];代替List<object> - 注释掉后仍然是2 GB,取消注释后变成了420 MB。 - ren
尝试一些简单的东西:List<byte>AddRange。(不要忘记使用一些实际大小来初始化列表)。 - Stefan Steinegger
@CodeCaster,我只是不明白为什么差别这么大,我可以理解可能会多4倍的内存,但54个字节而不是4个字节,感觉有些不对劲...另外那个内存峰值,在最后出现的,你能解释一下吗? - ren
1个回答

9
这是在分配大量微小对象时常见的问题:对象开销,通常可以忽略不计,但现在变得很重要了。
假设四个字节的数组会占用四个字节的内存是错误的。除了四个字节的有效负载外,数组对象必须存储数组的长度、同步块和类型指针。这意味着32位系统上需要12字节的开销,64位系统上则需要24字节。
除了单个数组对象的开销,还需要考虑到内存分配器的开销、内存对齐的开销和垃圾回收器的开销。综合起来,使用的总内存增长到2 Gb也并不是不合理的。
解决这个问题的方法之一是切换到一个uint列表,每个uint占用4个字节。当你需要存储4个字节时,将其转换为uint,并将其存储在列表中。当你需要取回你的字节时,将uint转换为一个临时的4字节数组。使用BitConverter来处理uintbyte数组之间的转换。

1
哦,出乎意料的是,这并没有减少内存使用量,我在问题中放了一张截图。 - ren
1
@ren 你使用 List<object> 还是 List<uint>?对象列表会带来“装箱”值的开销,这接近于数组的开销。而 uint 列表的开销要小得多。 - Sergey Kalinichenko

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