为什么当物理内存使用率超过95%时,.NET不会将未使用的内存释放回操作系统?

7
我们正在测试一个4进程的WCF IIS应用程序(x3版本),以了解其内存稳定性(泄漏),方法是每隔约1秒钟像负载均衡器一样对其进行ping。如果服务器上没有运行其他内容,则它可以正常运行超过12小时。
但是,如果我们故意减少可用总内存(固定页面,减少物理内存,启动其他应用程序)并将物理内存使用率推到97%并保持5分钟或更长时间,通常Windows会感知到这种情况并关闭其中一个进程。请注意,如果使用固定页面文件将内存推到97%,它也会失败。
然而,使用RedGate工具分析幸存进程之一的内存占用情况如下:

RedGate Summary Tab

由于请求只是一个稳定的ping,当服务器处于饱和状态时,似乎没有实际理由让.NET保留269MB的空闲内存。大约50%的IIS进程似乎处于这种状态(~1.8GB)。
该应用程序编译为.NET 4.0,gcServer设置为true。IIS网关检查设置为0%(minFreeMemoryPercentageToActivateService="0"),尽管在生产中我们可能会将其设置为2%。
服务器是2008 R2,物理内存约为4GB,固定页面为4GB,经过4.0和4.5.1测试(无关紧要)。
有一个类似问题的答案@atanamir声称:
“.NET将在物理内存不足时将其堆释放回操作系统。”
有人知道这个声明的任何参考吗?它可能是特定版本的吗?
参考资料:

我们后来在gcTrimCommitOnLowMemory上找到了这个链接。我们在aspnet.config、machine.config和system web.config中尝试了它。在system web.config中似乎有一些效果,但是当所有进程应该接近相同时,在95%的提交区域仍然有一些进程使用了2倍于其他进程的内存,并且似乎放弃了修剪。 - crokusek
2个回答

7

这并不完全回答了你提出的问题(为什么),但它应该是实现你想要做的事情的一种方法。

.NET Framework 4.5有一些新特性- source

一旦站点运行,垃圾收集器(GC)堆的使用可能成为其内存消耗的重要因素。像任何垃圾收集器一样,.NET Framework GC在CPU时间(收集频率和重要性)和内存消耗(用于新对象、释放或可释放对象的额外空间)之间进行权衡。对于以前的版本,我们提供了如何配置GC以实现正确平衡的指导。 对于.NET Framework 4.5,不再提供多个独立设置,而是提供了一个工作负载定义的配置设置,可以启用所有先前推荐的GC设置以及提供额外性能的新调整,以适应每个站点的工作集。例如,无需设置gcServer、gcConcurrent等。

此外here他们声明:

调整GC以适应高密度Web托管:GC会影响网站的内存消耗,但可以进行调整以实现更好的性能。您可以调整或配置GC以获得更好的CPU性能(降低收集频率)或更低的内存消耗(即更频繁的收集以更快地释放内存),从而实现每个站点更小的内存消耗(工作集)。为了启用GC内存调整,请将以下设置添加到Windows\Microsoft.NET\Framework\v4.0.30319\aspnet.config文件和Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.config文件中:
<configuration>
<!-- ... -->
<runtime>
<performanceScenario value="HighDensityWebHosting"  />
<!-- ... -->  

基本上,根据我所做的测试,我发现它会消耗更多的CPU,更少的内存,并使GC在清理和释放内存过程方面更加积极。 我们在我们的基础设施(IIS 7.5,新的4.5框架)中测试了这个设置,结果令人印象深刻。高内存使用导致的内存不足异常不再是问题。
希望这有所帮助。

1

atanamir提供的信息可以在MSDN页面上找到。

垃圾回收会在以下情况下发生:

系统物理内存不足。

托管堆上分配对象使用的内存超过可接受的阈值。此阈值会随着进程运行而不断调整。

调用GC.Collect方法。在几乎所有情况下,您不需要调用此方法,因为垃圾回收器会持续运行。此方法主要用于特殊情况和测试。


3
垃圾回收真的等同于“释放回操作系统”吗? - crokusek
2
@crokusek 是的,你有看我为你提供的那个页面吗?如果有的话,你会看到这一段(重点是我的):垃圾收集器还会根据需要保留段,并通过调用Win32 VirtualFree函数将(已清除任何对象的)段释放回操作系统。 - itsme86
1
不,我跳过了链接,认为答案中引用了相关部分。您的评论很有帮助,而且我没有给您点“踩”。然而,问题标题是关于为什么“释放段回到操作系统”没有按照广告所说的那样发生。 - crokusek
@itsme86 你写道:“垃圾回收器还会根据需要保留内存段,并将内存段释放回操作系统” - 这是过于简化了:在 x64 上,.NET CLR 内存段的大小为 2GB,因此 .NET CLR 必须确信整个 2GB 的内存段不再被使用,才会将其释放回操作系统。当然,未使用的内存页面(页面通常为 4KB 或 2MB)最终也会被操作系统 _分页_,即使没有 CLR 通知 Windows 页面或段的使用情况。 - Dai

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