从列表<T>和 IList <T>创建枚举器时的 GC 集合



我在https://softwareengineering.stackexchange.com/a/411324/109967:

这里还有另一个警告:如果通过接口访问,因此如果通过IList或IEnumerable枚举。你必须坚持以避免分配。

我想用这个.NET 5控制台应用程序测试这个理论(我试过在List<T>IList<T>之间交换(:

using System;
using System.Collections.Generic;
namespace ConsoleApp10
{    
class Program
{
static void Main(string[] args)
{
IList<int> numbers = new List<int> { 0, 1, 2, 3, 4, 5 };
//List<int> numbers = new List<int> { 0, 1, 2, 3, 4, 5 };
for (int i = 0; i < 2000; i++)
{
foreach (var number in numbers)
{
}
}
GC.Collect();
Console.WriteLine("GC gen 0 collection count = " + GC.CollectionCount(0)); //Always 1
Console.WriteLine("GC gen 1 collection count = " + GC.CollectionCount(1)); //Always 1
Console.WriteLine("GC gen 2 collection count = " + GC.CollectionCount(2)); //Always 1

}
}
}

似乎所有几代人都吃饱了,都获得了普通中等教育证书。

如果List<T>使用结构Enumerator,那么在调用GC.Collect()之前不应该发生任何堆分配(之后在对Console.WriteLine()的调用中创建字符串对象,但这发生在GC运行之后(。

问题1:为什么在使用List<T>时会进行堆分配?

问题2:我知道在通过IList<T>接口迭代List<T>时使用的引用类型Enumerator会导致堆分配(假设链接问题中的注释是正确的(,但为什么在所有3代中都会发生集合?

2000枚举器对象是很多对象,但一旦foreach循环完成,它就可以进行GCed了,因为foreach完成后没有任何对象引用该对象。为什么这些物体会进入第1代和第2代?

我认为GC.CollectionCount()向您展示的内容让您感到困惑。

每次调用GC.Collect()时,都会强制生成所有代的完整集合。然后调用GC.CollectionCount(),它向您显示所有世代都刚刚被收集。您只是在观察调用GC.Collect()的效果!

的确,IList版本正在分配枚举器,但这些枚举器在第0代中正在迅速消亡。

解决这类问题的合适工具是带有MemoryDiagnoser的BenchmarkDotNet。

我建立了一个简单的基准:

public static class Program
{
public static void Main()
{
BenchmarkRunner.Run<Benchmarks>();
}
}
[MemoryDiagnoser]
public class Benchmarks
{
[Benchmark]
public int IList()
{
int sum = 0;
IList<int> numbers = new List<int> { 0, 1, 2, 3, 4, 5 };
for (int i = 0; i < 2000; i++)
{
foreach (var number in numbers)
{
sum += number;
}
}
return sum;
}
[Benchmark]
public int List()
{
int sum = 0;
List<int> numbers = new List<int> { 0, 1, 2, 3, 4, 5 };
for (int i = 0; i < 2000; i++)
{
foreach (var number in numbers)
{
sum += number;
}
}
return sum;
}
}

这产生了以下结果:

BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.985 (20H2/October2020Update)
Intel Core i5-6300U CPU 2.40GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
.NET SDK=5.0.300-preview.21180.15
[Host]     : .NET 5.0.5 (5.0.521.16609), X64 RyuJIT
DefaultJob : .NET 5.0.5 (5.0.521.16609), X64 RyuJIT
>StdDev:right;">已分配
方法平均值错误
IList149.42μs
列表48.98μs

最新更新