【发布时间】:2022-02-03 10:00:16
【问题描述】:
关于垃圾收集器的另一个问题。关于 .net 环境中垃圾收集器的行为有很多问题,但很少有明确的答案。我的问题很简单,我希望如果有人能清楚地回答它应该可以帮助很多会经过这里的人。
所以,这是一个简单的代码(.net core 6.0.101)
var arraySize = int.MaxValue / 4;
var rnd = new Random();
var arrays = new List<byte[]>();
// Initial memory footprint
Console.WriteLine($"[0] Memory: {GC.GetTotalMemory(false) / 1000000:N0} MB");
for (int i = 1; i < 5; i++)
{
var array = new byte[arraySize];
// fill the array to be sure that .net doesn't do obscure memory optimizations
rnd.NextBytes(array);
arrays.Add(array);
}
Console.WriteLine($"[Before GC] Memory: {GC.GetTotalMemory(true) / 1000000:N0} MB");
arrays.Clear();
arrays = null;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
GC.WaitForPendingFinalizers();
Console.WriteLine($"[After GC] Memory: {GC.GetTotalMemory(true) / 1000000:N0} MB");
Console.WriteLine("Press a key to exit");
Console.ReadLine();
现在是结果。
C:\testsgc>dotnet run
[0] Memory: 0 MB
[Before GC] Memory: 2 147 MB
[After GC] Memory: 536 MB
Press a key to exit
进程内存占用(任务管理器):
- 初始内存:5.6 MB
- 填充变量后:2 056,5 MB
- 垃圾收集后:520,1 MB
在启动时,该进程占用 5,6 MB 或 RAM。一个字节数组需要大约 500 MB 的内存。该列表被清除并分配为 null(不需要)。
那么问题来了,如何释放最后一个字节数组?
只有精确和可验证的答案会被标记为已解决。
【问题讨论】:
-
尝试在
WaitForPendingFinalizers之后运行另一个GC.Collect。此外,请确保您没有在附加调试器的情况下运行,并且您是为 Release 而不是为 Debug 构建的。变量的作用域生命周期被人为地延长,以支持在变量最后一次使用后命中断点并且仍然能够检查它,例如,循环中的array变量可能会一直持续到方法结束。 -
感谢@LasseV.Karlsen 的帮助!不幸的是,我可以确认程序在没有附加任何调试器的情况下运行,并且我还尝试在
WaitForPendingFinalizers之后运行GC.Collect,但没有任何成功。我什至尝试了一个 for 循环或 100 次 GC.Collect 迭代。 -
我无法确切告诉您为什么会这样,但是当您运行最终报告时,您会将这 5 个数组中的 1 个保留在内存中。添加额外的 GC.Collect 没有帮助,但在 Release 构建中运行会。这很可能是我在此处发挥的其他评论中提到的人为延长寿命。但是,即使在循环内将
array设置为null也不会更改调试版本的这一点。因此,我无法解释为什么数组仍在内存中。但是,在var array = new之后添加其中一个 Console.WriteLine 调用会显示完全相同的内存使用情况。 -
试试
dotnet run --configuration Release -
配置发布完成了这项工作!
标签: c# memory garbage-collection