【发布时间】:2009-11-26 13:49:50
【问题描述】:
我正在研究 C# 中的垃圾收集器(或者更确切地说是 CLR?),试图更好地理解 C# 中的内存管理。
我制作了一个小示例程序,将三个较大的文件读入byte[] 缓冲区。我想看看,如果
- 我实际上需要做任何事情来提高内存效率
- 在当前迭代结束后将
byte[]设置为 null 时会产生任何影响 - 最后是否在通过
GC.Collect()强制垃圾收集时有帮助
免责声明:我使用 Windows 任务管理器测量了内存消耗并四舍五入。我试了几次,但总体还是一样。
这是我的简单示例程序:
static void Main(string[] args)
{
Loop();
}
private static void Loop()
{
var list = new List<string>
{
@"C:\Users\Public\Music\Sample Music\Amanda.wma", // Size: 4.75 MB
@"C:\Users\Public\Music\Sample Music\Despertar.wma", // Size: 5.92 MB
@"C:\Users\Public\Music\Sample Music\Distance.wma", // Size: 6.31 MB
};
Console.WriteLine("before loop");
Console.ReadLine();
foreach (string pathname in list)
{
// ... code here ...
Console.WriteLine("in loop");
Console.ReadLine();
}
Console.WriteLine(GC.CollectionCount(1));
Console.WriteLine("end loop");
Console.ReadLine();
}
对于每个测试,我只更改了foreach 循环的内容。然后我运行程序,在每个Console.ReadLine() 我停下来检查Windows任务管理器中进程的内存使用情况。我记下了使用的内存,然后用 return 继续程序(我知道断点;))。就在循环结束后,我向控制台写了GC.CollectionCount(1),以查看GC 跳入的频率。
结果
测试 1:
foreach ( ... )
{
byte[] buffer = File.ReadAllBytes(pathname);
Console.WriteLine ...
}
结果(使用的内存):
before loop: 9.000 K
1. iteration: 13.000 K
2. iteration: 19.000 K
3. iteration: 25.000 K
after loop: 25.000 K
GC.CollectionCount(1): 2
测试 2:
foreach ( ... )
{
byte[] buffer = File.ReadAllBytes(pathname);
buffer = null;
Console.WriteLine ...
}
结果(使用的内存):
before loop: 9.000 K
1. iteration: 13.000 K
2. iteration: 14.000 K
3. iteration: 15.000 K
after loop: 15.000 K
GC.CollectionCount(1): 2
测试 3:
foreach ( ... )
{
byte[] buffer = File.ReadAllBytes(pathname);
buffer = null;
GC.Collect();
Console.WriteLine ...
}
结果(使用的内存):
before loop: 9.000 K
1. iteration: 8.500 K
2. iteration: 8.600 K
3. iteration: 8.600 K
after loop: 8.600 K
GC.CollectionCount(1): 3
我不明白的:
- 在测试 1 中,内存随着每次迭代而增加。因此我猜想在循环结束时内存没有被释放。但是 GC 仍然说它收集了 2 次 (
GC.CollectionCount)。怎么样? - 在测试 2 中,将
buffer设置为null显然会有所帮助。内存比测试 2 低。但为什么GC.CollectionCount输出 2 而不是 3? 为什么内存使用没有测试 3 中那么低? - 测试 3 使用的内存最少。我会说是因为 1. 对内存的引用被删除(
buffer设置为null),因此当通过GC.Collect()调用垃圾收集器时,它可以释放内存。看起来很清楚。
如果有更多经验的人可以对以上几点有所了解,那真的会对我有所帮助。很有趣的话题恕我直言。
【问题讨论】:
-
非常大的概念,我建议您阅读重复问题的答案中提供的链接。
-
也许您可能还想尝试通过使用不同的 GC.Collect() 重载来指定 GCCollectionMode,详见 msdn 文章 Induced Collections (msdn.microsoft.com/en-us/library/bb384155.aspx)
-
您是否检查过#1 和#2 之间生成的IL 的差异?它们都包含数组的
.locals,还是只包含#1? -
确保您了解为什么查看任务管理器内存计数是完全错误的事情。如果你在看一个停车场,有趣的问题是里面有多少辆车,需要多长时间才能找到一个停车位,车辆靠得有多近,但你看到的是停车场的总面积,这汽车离开时不会变小。
标签: c# garbage-collection