You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么List占用的内存几乎是同容量数组的3倍?

内存开销差异的原因
  • List默认的动态扩容机制带来的预留空间浪费
    List内部基于数组实现,默认无容量参数初始化时初始容量为4,每次元素数超过当前容量就会将内部数组扩容为原来的2倍。写入2000万条float时,扩容过程最后一步会将容量从16777216(224)翻倍到33554432(225),此时内部数组实际占用128MB(33554432*4字节),但仅使用了前2000万条对应的76MB空间,剩下的空间属于预留未使用的冗余空间。
  • 未回收的旧扩容数组带来的临时内存占用
    每次扩容都会生成新的更大的数组,拷贝完旧数组元素后,旧数组需要等待GC回收。如果内存统计时GC还未执行,最大的旧数组(16777216容量,对应64MB)还会驻留在内存中,叠加后就会出现观测到的206MB左右的总占用。
  • 补充:List本身的对象开销可忽略,只有对象头、容量、元素数、内部数组引用几个字段,总共仅几十字节,不是内存开销的主要来源。
低开销优化方案
  • 方案1:预分配容量(最优,适合提前知道元素总数的场景)
    初始化List时直接指定总容量,完全避免扩容过程和预留空间浪费,内存占用和纯数组几乎一致。
    代码示例:
// 初始化时直接指定2000万的容量
List<float> listValues = new List<float>(20000000);

for (int i = 0; i < 20000000; i++)
     listValues.Add(i);
  • 方案2:写入完成后裁剪多余空间(适合提前不知道元素总数的场景)
    所有元素写入完成后调用TrimExcess()方法,会将内部数组的容量调整为和实际元素数一致,释放多余的预留空间。注意如果后续还要新增元素,会触发重新扩容,适合一次性写入的场景。
    代码示例:
List<float> listValues = new();

for (int i = 0; i < 20000000; i++)
     listValues.Add(i);

// 裁剪多余容量
listValues.TrimExcess();
  • 方案3:直接使用数组(适合完全不需要动态增减元素的场景)
    如果场景不需要后续新增、删除元素,直接使用float数组是内存效率最高的选择,没有任何额外封装开销。
  • 方案4:使用数组池(适合高频重复创建大集合的场景)
    如果需要频繁创建和销毁大的数值集合,可以用ArrayPool<float>租用数组,用完归还,避免频繁大对象GC带来的性能损耗。

内容的提问来源于stack exchange,提问作者Zelos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 16:06:04