之前在开发中用到 List 的时候几乎就是拿过来就用,从来没有考虑过 List 的内存分配问题,试想一个有 10 万元素的 List 在构造和添加元素时内存是如何变化的呢?
在 MSDN 上关于 List 的 Capacity 属性是这么解释的:“获取或设置该内部数据结构在不调整大小的情况下能够容纳的元素总数”。当我们添加的元素数量小于等于 Capacity 的值时,List 是不会重新调整内部数据结构,也就是不会重新申请或者分配内存,而当我们添加的元素数量大于 Capacity 的值时,List 就会不断的调整内部数据结构或者重新申请分配内存,这样的话对效率肯定会有一定的影响的。
当我们使用 List list = new List() 实例化一个List对象是,.Net Framework 只是在内存中申请了一块内存在存放 list 对象本身,系统此时并不知道 list 会有多少 item 元素。当我们向 list 添加第一个 item 时,list 会申请能存储 4 个 item 元素的存储空间,此时 Capacity 是 4,但是当我们添加到第五个 item 时,此时的 Capacity 就会变成 8,也就是当 list 发现元素的总数大于 Capacity 数量时,会主动申请并重新分配内存,当我们添加到第九个 item 时,Capacity 不是 12 而是 16,也就是说 list 每次申请的内存数量都是之前 item 元素数量两倍。然后将当前所有的 item 元素和新添加的元素复制到新的内存。
大家可以看到,如果 list 需要添加的元素特别多时,list 会不断地申请新内存,复制已有元素和新加元素到新内存,这个过程会产生资源的浪费及性能问题。
如果当设置的 Capacity 值远大于 list 的实际元素数量时,应使用 TrimExcess() 方法释放掉未使用的内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public class Part { public string PartName { get; set; } public int PartId { get; set; } public override string ToString() { return "ID: " + PartId + " Name: " + PartName; } }
class Program { static void Main(string[] args) { List<Part> parts = new List<Part>();
Console.WriteLine("\nCapacity: {0}", parts.Capacity);
parts.Add(new Part() { PartName = "crank arm", PartId = 1234 }); parts.Add(new Part() { PartName = "chain ring", PartId = 1334 }); parts.Add(new Part() { PartName = "seat", PartId = 1434 }); parts.Add(new Part() { PartName = "cassette", PartId = 1534 }); parts.Add(new Part() { PartName = "shift lever", PartId = 1634 });
Console.WriteLine();
foreach (Part aPart in parts) { Console.WriteLine(aPart); }
Console.WriteLine("\nCapacity: {0}", parts.Capacity); Console.WriteLine("Count: {0}", parts.Count);
parts.TrimExcess(); Console.WriteLine("\nTrimExcess()"); Console.WriteLine("Capacity: {0}", parts.Capacity); Console.WriteLine("Count: {0}", parts.Count);
parts.Clear(); Console.WriteLine("\nClear()"); Console.WriteLine("Capacity: {0}", parts.Capacity); Console.WriteLine("Count: {0}", parts.Count); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 输出结果: Capacity: 0
ID: 1234 Name: crank arm ID: 1334 Name: chain ring ID: 1434 Name: seat ID: 1534 Name: cassette ID: 1634 Name: shift lever
Capacity: 8 Count: 5
TrimExcess() Capacity: 5 Count: 5
Clear() Capacity: 5 Count: 0
|
知道了 list 的 Capacity 及 TrimExcess() 方法的用处,保证有限的内存空间能够得到合理的运行,归纳起来主要有以下几点:
- 当我们实例化一个 List 对象时,如果知道最大的 Item 元素时,应该在实例化 List 时指定 Capacity 的数量,直接使用 List 的构造方法 public List(int capacity) 就可以。
- 当由于不断的从 list 中 remove 掉大量元素时,此时 list 内存仍占用一部分不需要使用的空间,造成内存的浪费,此时可以调用 TrimExcess() 方法来释放多余的内存。
参考:https://www.cnblogs.com/jimmy-y/p/5327950.html