.NET进程分配的内存何时释放回Windows系统?

21

配置

.NET为每个代的堆(0、1、2、LOH)分配内存段,以获得连续的内存块,在启动时和在尝试满足分配请求或集合后。除了可能会出现第二代和大对象堆之外,为每个堆分配的内存可能会随着应用程序“热身”而稳定。

在垃圾回收期间,每个堆(0、1、2)都被扫描和压缩,但大对象堆(LOH)只是被扫描。

我理解集合中的“扫描”部分是指GC识别不再与根连接并可供收集(或完成处理)的对象,“压缩”意味着仍然存在于堆中的地址进行重新组织,使剩余的可用堆具有更多连续的可用内存。

当堆内的每个段的预算超出时,.NET将分配另一个段,以便在可能的情况下满足分配。

问题

我的问题是:在每个堆中,如果应用程序不再使用(调用)其保留的内存,那么这些内存何时被释放回操作系统?

我认为这种情况可能会导致进程似乎消耗了大量内存(虚拟大小非常大,但私有字节很小),但检查其堆时大部分空间都是空闲的。另外,堆的总大小也可能相当小,不能解释进程正在消耗的内存。

没有被阻塞的终结器,所有内容都看起来健康 - 可能在触发监视器警报(例如)之前运行了数周。

为了进一步澄清问题,如果您阅读Tess的.NET内存管理——餐厅类比,如果桌子代表堆段,那么这家餐厅会失去桌子吗(例如,释放堆段)?

编辑

  1. 删除了有关工作集和鸡的混淆引用
  2. 添加了对Tess餐厅类比的引用

我认为最小化不会要求任何人保留内存(如果这是有时发生的事情)。据我所知,最小化只会导致Windows更渴望将该进程的内存交换到磁盘上。 - R. Martinho Fernandes
请查看以下网站:http://www.informit.com/articles/article.aspx?p=100597。这被称为非确定性终结 - 无法确定对象的终结程序何时被调用。结果是,您的对象使用的任何内存或非托管资源在对象本身实际上不再被您的程序使用后仍保留分配了一段不确定的时间。 - Marek Kwiendacz
@Martinho,Winform 可能会挂钩最小化消息并调用GC.Collect(),我不确定但是有可能。 - Ian Ringrose
如果Windows确实有一种机制来要求进程释放一些未使用的内存,我从未听说过,并且我编写的所有应用程序都会忽略Windows的任何这样的请求,因为我从未编写处理它的代码。我敢打赌很多应用程序也会以同样的方式行事。当我不需要内存时,我会释放它。我不会等待Windows来麻烦地问:“嘿,我需要那个内存,把它还给我!”如果它来要求它,我没有可以归还的内存。这似乎没有太多意义。我可能是错的。 - R. Martinho Fernandes
1
@Martinho - 你是对的,我误记的是当最小化一个应用程序时,进程“工作集”受到影响的报告。 - Zach Bonham
显示剩余2条评论
2个回答

10
我的答案是 - 这并不重要。操作系统为应用程序(在其中运行.NET runtime)提供了虚拟内存。这不是“真正”的内存。操作系统可以将虚拟内存的每个页面放在任何地方 - 在处理器上、主内存中或磁盘上。因此,一个应用程序可能使用比系统上RAM更多的内存,并且操作系统会将需要的位从磁盘复制到内存中,以确保应用程序继续运行(除了某些寻址和技术限制)。
操作系统管理系统上所有进程的虚拟内存,并确保一个程序不会占用系统上的所有RAM,从而损害其他程序的运行。当.NET runtime从系统请求内存用于堆的使用时,但然后并没有使用它时,这些内存(如果系统上的可用RAM较少)将被移动到磁盘上,因为它们没有被访问过。
通过电子邮件澄清Tess的解释(我强调):
段大小在应用程序的整个过程中保持不变,但有两件事情需要考虑。
1.分配段是一种虚拟分配,这意味着虽然我们保留虚拟内存,但我们只提交我们实际使用的部分,因此段所用的私有字节与段大小不同,这意味着在垃圾回收后,您的私有字节将下降,而虚拟字节仍将保持不变。
2.当一个段不再使用时,即如果您发生了在一个段中进行GC以使其不再包含任何.NET对象,则虚拟分配将返回到操作系统。
据此,堆段(餐厅桌子)将被归还给操作系统。

2
虽然我基本上同意,尤其是关于“这没关系”的部分,但这种行为确实会影响某些东西:交换文件的大小。 - R. Martinho Fernandes
1
thecoop:在大硬盘上交换文件大小并不重要,但是磁盘访问性能仍可能成为问题——因此更快(更智能)地释放内存,然后使用较小的交换文件对于性能更好。 - Marek Kwiendacz
1
我恰好运行在一个5GB分区上的系统,交换文件也在其中。交换文件增加1GB将会很重要。就像RAM一样,交换空间也不是无穷无尽的资源。虽然它很少有影响,在你的应用程序中不是你需要担心的事情,但说它“不会真正影响任何事情”并不完全正确。 - R. Martinho Fernandes
只是为了让我们在同一个“页面”上(巴达砰!),交换文件是一个共享资源,因为所有进程都会利用它。一个5GB或[n]GB的交换文件,并不意味着我的应用程序可以使用全部 - 根据操作系统架构,我仍将遇到可寻址进程限制。Windows内存限制 - Zach Bonham
1
@thecoop - 我添加了来自 Tess 的带外电子邮件的注释,以澄清你的答案。如果这样做不合适,请告诉我。 - Zach Bonham
显示剩余3条评论

0

我不知道这个问题的答案,但我怀疑 .NET 直到进程退出(或者可能在卸载 AppDomain 时)才会释放其堆。这仅基于我观察 perfmon .NET 内存计数器得出的结论。在我的应用程序中,GC 总字节数计数器显示,保留字节在我的应用程序生命周期内会上下波动,大约为30MB。

而且,如果 GC 在服务器模式下运行,可能 .NET 更经常地释放内存。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接