高内存消耗与Enumerable

高内存消耗与Enumerable

本文介绍了高内存消耗与Enumerable.Range?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

本来我想知道是否了ToList 分配更多的内存比使用名单,其中的构造; T> 这需要一个的IEnumerable< T> (无差异)。

有关测试的目的,我用 Enumerable.Range 来创建一个源阵列,我可以用它来创建名单,其中的一个实例; INT> 通过1。了ToList 和2的。两者都创建副本。

这是我如何来发现的内存消耗有很大的区别:

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

在我使用的第一和调用了ToList 生成的对象ineeds〜比阵列(38,26MB / 64MB),60%以上的内存。

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

  VAR memoryBefore = GC.GetTotalMemory(真正的);
VAR范围= Enumerable.Range(1,10000000);
VAR rangeMem = GC.GetTotalMemory(真) -  memoryBefore; // 微不足道
VAR列表= range.ToList();
VAR memoryList = GC.GetTotalMemory(真) -  memoryBefore  -  rangeMem;

字符串memInfoEnumerable =的String.Format(记忆之前:{0:N2} MB列表:{1:N2} MB
    (memoryBefore / 1024f)/ 1024f
    (memoryList / 1024f)/ 1024f);
//记忆前:0,11 MB列表:64,00 MB

memoryBefore = GC.GetTotalMemory(真正的);
变种阵列= Enumerable.Range(1,10000000).ToArray();
VAR memoryArray = GC.GetTotalMemory(真) -  memoryBefore;
表= array.ToList();
memoryList = GC.GetTotalMemory(真) -  memoryArray;

字符串memInfoArray =的String.Format(记忆之前:{0:N2} MB数组:{1:N2} MB列表:{2:N2} MB
   (memoryBefore / 1024f)/ 1024f
   (memoryArray / 1024f)/ 1024f
   (memoryList / 1024f)/ 1024f);
//记忆之前:64,11 MB阵:38,15 MB列表:38,26 MB
 

解决方案

这可能涉及到使用添加到列表中时,调整为后盾缓存加倍算法。当您分配一个数组,那长度被称为,然后可以通过检查 IList的查询[< T>] 和/或的ICollection [< T>] ;因此,它可以分配一个阵列,大小合适的第一次,然后就块复制的内容。

随着序列,这是不可能的(顺序不公开以任何方式访问的长度);因此,它必须改为退回到保持填补了缓冲;如果已满,则双击它,然后复制。

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

这是有趣的测试将是:

  VAR列表=新的名单,其中,INT>(10000000);
list.AddRange(Enumerable.Range(1,10000000));
 

这将首先分配适当的大小,同时仍使用顺序。

TL;博士;构造,当传递的序列,首先检查,看它是否可以转换为一公知的界面获得的长度。

Originally i wanted to know whether ToList allocates more memory than using the constructor of List<T> which takes an IEnumerable<T> (no difference).

For test purposes i used Enumerable.Range to create a source array that i could use to create an instance of List<int> via 1.ToList and 2.constructor. Both are creating copies.

This is how I came to notice a great difference in memory consumption between:

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

When i use the first and call ToList the resulting object ineeds ~60% more memory than the Array(38,26MB/64MB).

Q: What is the reason for this or where is my error in reasoning?

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"
解决方案

This probably relates to the doubling algorithm used to resize the backing buffer when adding to a list. When you allocate as an array, the length of that is known, and can be queried by checking for IList[<T>] and/or ICollection[<T>]; thus it can allocate a single array, right-sized the first time, and then just block-copy the contents.

With the sequence this is not possible (the sequence does not expose the length in any accessible way); thus it must instead fall back to "keep filling up the buffer; if full, double it and copy".

Obviously this needs approx double the memory.

An interesting test would be:

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

This will allocate the right size initially, while still using the sequence.

tl;dr; the constructor, when passed a sequence, first checks to see if it can obtain the length by casting to a well-known interface.

这篇关于高内存消耗与Enumerable.Range?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-02 03:07