dotnet C# - 为什么 GC 不释放内存?



我想问一下。net垃圾回收器在这种情况下是如何工作的。

我有一个静态函数,我在其中一次又一次地分配字节数组。我插入字节数组引用到列表中。对字节数组或列表的引用没有传递到函数外部。

public static void StressMem(TimeSpan duration)
{
var bgnTm = DateTime.Now;
var data = new List<byte[]>();
while (true)
{
var arr = new byte[1024];
data.Add(arr);
var difTm = DateTime.Now - bgnTm;
if (difTm > duration) break;
}
}

我期望在这个函数完成后释放内存(一段时间后)。但这不会发生在我身上。为什么会发生这种情况?

dotnet 5.0.302

原则上,GC可以随时自由运行,或者根本不运行。在实践中,我不希望它运行,除非你真的试图分配一些东西。因此,在StressMem返回之后,您可能看不到任何GC,除非您执行更多需要内存的工作。如果你在循环中运行StressMem,我预计会出现频繁的gc。

请注意,这并不一定意味着进程的内存使用将减少。如果物理内存不足,垃圾收集器可能会将内存返回给操作系统,但是如果您有空闲内存,为什么不使用它呢?

如果您正在调查您的应用程序如何使用内存,我建议使用内存和/或性能分析器。它们应该显示你用了多少时间来进行GC,你用了多少内存,你分配了多少内存等等。

试着运行下面的代码:

void Main()
{
GC.Collect();
long x = GC.GetTotalMemory(false);
StressMem(TimeSpan.FromMilliseconds(100.0));
long y = GC.GetTotalMemory(false);
GC.Collect();
long z = GC.GetTotalMemory(false);
Console.WriteLine(x);
Console.WriteLine(y);
Console.WriteLine(z);
}
public static void StressMem(TimeSpan duration)
{
var bgnTm = DateTime.Now;
var data = new List<byte[]>();
while (true)
{
var arr = new byte[1024];
data.Add(arr);
var difTm = DateTime.Now - bgnTm;
if (difTm > duration) break;
}
}

这是你用一些代码检查内存的确切方法。

我:

2278192
59759468
2278640

它显然是在分配内存,当GC被迫运行时,它会收集内存。

GC只在内存有压力时运行。它不是基于"一些时间"。

如果我理解正确的话,GC不会释放内存,除非它有理由。例如,如果我编写一个占用一些内存的程序,然后不做任何其他事情,那么该进程将占用内存并且很可能不再释放内存(除非直接调用GC.Collect())。我在一个小示例中尝试了它(见下文)。

public static void StressMem(TimeSpan duration)
{
var bgnTm = DateTime.Now;
var data = new List<byte[]>();
while (true)
{
var arr = new byte[1024];
data.Add(arr);
var difTm = DateTime.Now - bgnTm;
if (difTm > duration) break;
}
}
static void Main(string[] args)
{
var mem = GC.GetTotalMemory(false);
Console.WriteLine($"{DateTime.Now} [Begin] {mem}");
StressMem(TimeSpan.FromSeconds(5));
mem = GC.GetTotalMemory(false);
Console.WriteLine($"{DateTime.Now} [StressMem] {mem}");
var dt = DateTime.Now;
var lastPrint = dt;
while (true)
{
var now = DateTime.Now;
if (now - lastPrint > TimeSpan.FromSeconds(15))
{
lastPrint = now;
mem = GC.GetTotalMemory(false);
Console.WriteLine($"{DateTime.Now} {mem}");
}
if (now - dt > TimeSpan.FromMinutes(15)) break;
}
}

最新更新