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

dotnet C# - Why doesn't the GC free memory?

我想问一下 .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;
    }
}

我原以为当这个函数完成后内存会被释放(一段时间后)。但这不会发生在我身上。为什么会这样?

网络 5.0.302

原则上,GC 可以随时 运行 自由,或者根本不这样做。在实践中,我不希望它 运行 除非你真的想分配一些东西。所以在 StressMem returns 之后你可能看不到任何 GC,除非你做更多需要内存的工作。如果你 运行 StressMem 在一个循环中,我会期望频繁的 GC。

请注意,这并不一定意味着进程的内存使用量会减少。如果物理内存 运行 不足,垃圾收集器可能 return 内存到 OS,但是你有空闲内存吗,为什么不使用它?

如果您正在调查您的应用程序如何使用内存,我建议您使用内存 and/or 性能分析器。他们应该揭示您为 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 仅 运行s。它不是基于“某个时间”。

如果我没理解错的话,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;
   }
}