c# - Enumerable.Range 的高内存消耗?

标签 c# .net linq memory-management

本来我想知道是否ToList分配比使用 List<T> 的构造函数更多的内存这需要 IEnumerable<T> (没有不同)。

出于测试目的,我使用了 Enumerable.Range创建一个源数组,我可以用它来创建 List<int> 的实例通过 1. ToList和 2. constructor .两者都在创建副本。

这就是我如何注意到内存消耗的巨大差异:

  1. Enumerable.Range(1, 10000000)
  2. Enumerable.Range(1, 10000000).ToArray()

当我使用第一个并调用 ToList 时生成的对象需要比数组多 60% 的内存 (38,26MB/64MB)。

问:这是什么原因,或者我的推理错误在哪里?

var memoryBefore = GC.GetTotalMemory(true);
var range = Enumerable.Range(1, 10000000);
var rangeMem = GC.GetTotalMemory(true) - memoryBefore; // negligible
var list = range.ToList();
var memoryList = GC.GetTotalMemory(true) - memoryBefore - rangeMem;

String memInfoEnumerable = String.Format("Memory before: {0:N2} MB List: {1:N2} MB"
    , (memoryBefore / 1024f) / 1024f
    , (memoryList   / 1024f) / 1024f);
// "Memory before: 0,11 MB List: 64,00 MB"

memoryBefore = GC.GetTotalMemory(true);
var array = Enumerable.Range(1, 10000000).ToArray();
var memoryArray = GC.GetTotalMemory(true) - memoryBefore;
list = array.ToList();
memoryList = GC.GetTotalMemory(true) - memoryArray;

String memInfoArray = String.Format("Memory before: {0:N2} MB Array: {1:N2} MB List: {2:N2} MB"
   , (memoryBefore / 1024f) / 1024f
   , (memoryArray  / 1024f) / 1024f
   , (memoryList   / 1024f) / 1024f);
// "Memory before: 64,11 MB Array: 38,15 MB List: 38,26 MB"

最佳答案

这可能与添加到列表时用于调整后备缓冲区大小的加倍算法有关。当您分配为数组时,的长度是已知的,并且可以通过检查 IList[<T>] 来查询和/或 ICollection[<T>] ;因此它可以分配一个单一的数组,第一次调整大小,然后只是 block 复制内容。

对于序列这是不可能的(序列不会以任何可访问的方式公开长度);因此它必须退回到“继续填充缓冲区;如果已满,将其加倍并复制”。

显然这需要大约两倍的内存。

一个有趣的测试是:

var list = new List<int>(10000000);
list.AddRange(Enumerable.Range(1, 10000000));

这将在最初分配正确的大小,同时仍然使用序列。

tl;博士;构造函数在传递一个序列时,首先检查它是否可以通过转换为众所周知的接口(interface)来获取长度。

关于c# - Enumerable.Range 的高内存消耗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10519275/

相关文章:

c# - 如何通过来自多个程序集的接口(interface)有效地对所有实现进行分组?

c# - EF Core 如何选择具有多对多关系的实体

c# - ASP.NET 将 xml 字符串转换为字典

c# - 将 key 转换为 C# 中的 key

c# - 检查复选框 MVVM 中是否未选择任何项目

c# - MEF 2 : import many

c# - 使用反射获取基类的 protected 属性值

c# - 在循环中访问相似的命名属性

c# - 我应该在哪里放置try catch?

c# - 如何制作包含对 Any() 的调用的 System.Linq.Expressions.Expression 对象