如何释放 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
- 填充变量后:2 056,5 MB
- 垃圾收集后:520,1 MB
启动时,进程占用 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
,但这并没有改变任何东西。
关于垃圾收集器的另一个问题。关于 .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(不需要)。
那么问题来了,如何释放最后一个字节数组?
只有精确和可验证的答案才会被标记为已解决。
当您 运行 来自调试版本的程序时,您的 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
,但这并没有改变任何东西。