【问题标题】:Remaining RAM after all threads are done所有线程完成后剩余的 RAM
【发布时间】:2026-02-14 09:50:02
【问题描述】:

在 C# 中,假设您运行多个线程,使用大量对象和 RAM 进行大量计算。最后,所有线程都完成了。然后你运行GC.Collect()。你只是不要停止这个过程。

除非您在静态变量中存储对某些对象的引用,否则我希望最终使用的 RAM 会回到 0,除了加载的 DLL 或确实使用大量 RAM 的东西(很多意味着几 GB )。

是真的吗?

是否存在我们可能不知道的剩余大 RAM 使用量? (不包括错误代码导致的内存泄漏)

【问题讨论】:

  • 这感觉像是一个 XY 问题 - meta.stackexchange.com/questions/66377/what-is-the-xy-problem为什么你在乎?您要解决的实际、真实、潜在的问题是什么?
  • 另外,您有一个假设 - 当您测试它时发生了什么?
  • “除非你将某些对象的引用存储在静态变量中” 静态变量是内存中的变量,就像每个函数的本地变量一样。变量作用域只是一种编译时约定,如强类型化和类访问器,以避免许多可能的错误。分配给静态变量的任何内容都具有对应用程序根的完整的强引用链,这不符合收集条件。但我同意这听起来确实有点像 XY 问题。我们一般不做这种内存微优化。

标签: c# .net memory-management garbage-collection


【解决方案1】:

是否存在我们可能不知道的剩余大 RAM 使用量?

是的。实际上GC.Collect() 并没有收集所有分配的内存。它只收集都是“小”对象的第 0、1、2 代。

注意:甚至不清楚GC.Collect() 是否总是会导致第 0,1 和 2 代的完整垃圾收集。人们有不同的策略来处理它,但我不想详细说明因为我没有最终答案,而且它似乎取决于 .NET 版本。

还有另一个堆Large Object Heap,有时称为第 4 堆(甚至不是好名字的第 3 代),所有大小 >85000 的对象都被分配在这里。

这个堆有一个完全不同的功能。例如阅读:

大对象堆似乎被进程(Windows)保留,而实际上它充满了可供新(大)对象使用的“洞”,并且被 C# 内存管理器视为空闲。

在 .NET 4.5.1 之前无法手动压缩大对象堆。它仅在 CLR 需要/想要它时发生。

CLR 是否经常压缩 LOH 取决于您使用的垃圾收集器。通常,在 ASP.NET 中默认使用的 server GC 不会压缩 LOH。实际上这对于 .NET 4.5 来说是正确的,但可能会在下一个版本中演变。管理 LOH 的策略往往在 .NET 版本中发生了相当大的变化。并非所有曾经解释过的细节都是真实的。

据我所知,这可能是观察到几 GB 剩余 RAM(不是内存泄漏)的主要原因。大多数情况下,这不是问题。你会在这里找到更多:When is memory, allocated by .NET process, released back to Windows

【讨论】: