一文看懂C#中List的扩容机制

2020-05-29 22:00:50王冬梅

一:背景

1. 讲故事

在前一篇大内存排查中,我们看到了Dictionary正在做扩容操作,当时这个字典的count=251w,你把字典玩的66飞起,其实都是底层为你负重前行,比如其中的扩容机制,当你遇到几百万甚至千万的大集合这个扩容机制还真的需要挖一下,免的入戏太深,难以自拔。

二:List扩容机制

1. 如何查看

要想看它的扩容机制,可以用ILSpy去看看List的源码即可,非常简单。

从源码的 int num = (_items.Length == 0) ? 4 : (_items.Length * 2) 可以非常清楚的看到,4个空间起步,后面都是 *2 的扩容,也就说当你有 2^(n-1) + 1 个元素,实际上你需要占用 2^n个空间。

下面我用C#代码演示一下:

 static void Main(string[] args)
 {
  var list1 = Enumerable.Range(0, (int)Math.Pow(2, 22)).ToList();

  var list2 = new List<int>(list1);
  list2.Add(1);

  Console.WriteLine($"list1.Capacity={list1.Capacity}");
  Console.WriteLine($"list2.Capacity={list2.Capacity}");

  Console.ReadLine();
 }

 ------ output -------

list1.Capacity=4194304
list2.Capacity=8388608

从代码中可以看到,当List中已有 4194304个元素的时候,再往其中塞入一个元素,仅仅是多一个,在底层可是翻倍的空间占用哦,太可气了,要想往深处看可以用windbg看一下各自数组占用大小。

0:000> !DumpObj /d 000001ec037d2e20
Name: System.Collections.Generic.List`1[[System.Int32, mscorlib]]
Fields:
  MT Field Offset   Type VT Attr  Value Name
00007ffde2ac8538 400189e 8 System.Int32[] 0 instance 000001ec147b9c10 _items
00007ffde2ac85a0 400189f 18  System.Int32 1 instance  4194304 _size
00007ffde2ac85a0 40018a0 1c  System.Int32 1 instance  4194304 _version
00007ffde2ac5dd8 40018a1 10 System.Object 0 instance 0000000000000000 _syncRoot
00007ffde2ac8538 40018a2 0 System.Int32[] 0 shared  static _emptyArray
     >> Domain:Value dynamic statics NYI 000001ec01bc0920:NotInit <<

0:000> !do 000001ec147b9c10
Name: System.Int32[]
MethodTable: 00007ffde2ac8538
EEClass: 00007ffde2c35918
Size: 16777240(0x1000018) bytes
Array: Rank 1, Number of elements 4194304, Type Int32 (Print Array)
Fields:
None


0:000> !do 000001ec037d2e78
Name: System.Collections.Generic.List`1[[System.Int32, mscorlib]]
MethodTable: 00007ffde2ada068
EEClass: 00007ffde2c3b008
Size: 40(0x28) bytes
File: C:WINDOWSMicrosoft.NetassemblyGAC_64mscorlibv4.0_4.0.0.0__b77a5c561934e089mscorlib.dll
Fields:
  MT Field Offset   Type VT Attr  Value Name
00007ffde2ac8538 400189e 8 System.Int32[] 0 instance 000001ec167b9c80 _items
00007ffde2ac85a0 400189f 18  System.Int32 1 instance  4194305 _size
00007ffde2ac85a0 40018a0 1c  System.Int32 1 instance  1 _version
00007ffde2ac5dd8 40018a1 10 System.Object 0 instance 0000000000000000 _syncRoot
00007ffde2ac8538 40018a2 0 System.Int32[] 0 shared  static _emptyArray
     >> Domain:Value dynamic statics NYI 000001ec01bc0920:NotInit <<
0:000> !do 000001ec167b9c80
Name: System.Int32[]
MethodTable: 00007ffde2ac8538
EEClass: 00007ffde2c35918
Size: 33554456(0x2000018) bytes
Array: Rank 1, Number of elements 8388608, Type Int32 (Print Array)
Fields:
None