如何释放 C# 进程中所有使用的 RAM?

How to free up all used RAM in a C# process?

关于垃圾收集器的另一个问题。关于 .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 或 RAM。一个字节数组大约需要 500 MB 的内存。列表被清除并分配给 null(不需要)。

那么问题来了,如何释放最后一个字节数组?

只有精确可验证的答案才会被标记为已解决。

当您 运行 来自调试版本的程序时,您的 sub-arrays 仍然保留在内存中。

让我们稍微调整一下您的程序,在您的循环中添加一个 Console.WriteLine,如下所示:

for (int i = 1; i < 5; i++)
{
    var array = new byte[arraySize];
    Console.WriteLine($"[In loop] Memory: {GC.GetTotalMemory(true) / 1000000:N0} MB");

您的程序的输出现在变为:

> dotnet run
[0] Memory: 0 MB
[In loop] Memory: 536 MB
[In loop] Memory: 1 073 MB
[In loop] Memory: 1 610 MB
[In loop] Memory: 2 147 MB
[Before GC] Memory: 2 147 MB
[After GC] Memory: 536 MB

如您所见,循环中的第一个报告大小匹配。因此很可能其中一个数组在垃圾回收中幸存下来。

“解决此问题”的典型方法是在调用 GC.WaitForPendingFinalizers 之后添加另一个调用 GC.Collect,但这在这里没有区别。

不再使用的变量实际上有资格进行垃圾回收,但前提是您不 运行 使用调试器并在启用优化的情况下进行编译,也就是 Release-build.

因此,如果我们尝试仅 运行 来自 Release-build 的程序:

> dotnet run --configuration Release
[0] Memory: 0 MB
[Before GC] Memory: 2 147 MB
[After GC] Memory: 0 MB

虽然我无法解释为什么 sub-arrays 中的一个还活着并且还在踢,但它似乎是相关的。在将 array 添加到 arrays 列表后,我尝试在循环内将 array 设置为 null,但这并没有改变任何东西。