服务器模式GC似乎永远不会收集Gen 0堆

澄清的问题(tl; dr)

在阅读和分析下面介绍的所有结果之后,问题似乎归结为GC在服务器模式下不为我们的应用程序收集Gen 0堆,一旦它改为工作站模式,问题就消失了。

原始问题和细节

我的问题有点涉及: 这个问题和这个问题 。

我们最近在我们的测试环境中遇到了.NET应用程序中的内存泄漏问题,工作进程在负载下或在无负载时会迅速升至450MB左右。

这个问题无法在我们的开发环境中复制,主要区别在于开发环境是物理服务器,而测试环境是由Puppet虚拟化和控制的(除此之外我对环境本身并不了解)。

为了有希望看到哪些对象负责占用所有内存,我在测试服务器上运行了Ants Memory Profiler,我发现所有内存都保留为未使用且永远不会被释放。

在研究可能导致这种情况的原因时,我遇到了这个论坛post ,这反过来又引导我阅读这篇文章 。

我最后尝试了它建议的配置,将GC置于工作站模式:

      

在运行iisreset并重新运行我的内存分析后,问题完全消失了,这很好,但仍然没有真正解释首先发生的事情。

我确实做了一些阅读并发现了这个问题 ,这使我相信这种配置变化最终可能会对我们的应用程序的吞吐量产生不利影响。

所以我的问题是:什么会导致IIS工作进程累积大量未被垃圾收集的未使用内存?

编辑:为了更清楚地澄清我的问题,我相信我们已经certificate代码不对此负责,因为完全相同的代码在开发环境中不会遇到此问题。

以下是我在配置更改之前和之后的内存分析截图,这里没有很多信息,但图表确实显示了内存趋势。

在配置更改之前配置更改后

编辑2:这是我可以收集的服务器规格,我可能会得到更多只需要时间。

开发环境:物理机CPU:单核内存:6GB

测试环境:虚拟机CPU:4个逻辑线程(我无法评论CPU数量)内存:8GB

Machine.Config文件的唯一区别是开发环境正在为端点和服务行为添加“Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior”。

并且测试环境当前具有aspnet.config文件中先前提到的GC设置。

编辑3:做了一些更多的分析并注意到我可以在Ants中添加更多的计数器,特别是我添加了“Gen 0堆大小”,看起来这是问题的根源。 当GC处于服务器模式时,当我触发测试时,我正在用于分析此行,立即跳转到~300MB然后再回到~230MB但是从不会一直回落(下图)。

Gen 0之前

在工作站模式下使用GC运行相同的分析会看到Gen 0堆大小具有小得多的初始峰值,并在请求完成时返回到基本为零(下图)。

Gen 0 After

对此进行更多搜索会引出另一个更相关的SO问题,但是他的发现是这个内存使用是一个非问题,而在我的情况下,服务实际上需要每天至少手动重启一次。

我还发现这篇文章在这个问题上有以下几点说明(这似乎描述了几乎完美的情况:

第0代可能在64位系统上拥有更多的对象,尤其是当您使用服务器垃圾收集而不是工作站垃圾收集时。 这是因为在这些环境中触发第0代垃圾收集的阈值更高,并且第0代集合可以变得更大。 当应用程序在触发垃圾回收之前分配更多内存时,性能会得到改善。

虽然问题仍然存在,在服务器模式下,第0代堆似乎从未收集过,而不是经常收集。

不是一个直接的答案,更像是一个bandaid,但是如果你可以在w3wp进程中运行.Net 4.5.1 Code,你可以压缩LOH并且很多未使用的分配内存[可能]会减少。

您可以创建应用程序启动代码,该代码启动一个计时器,该计时器经常从w3wp.exe进程内部运行。

 GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GC.Collect(); 

直到4.5.1才会添加此function,因此您无法在不针对框架的至少4.5.1的.Net程序集中使用它。

这可能允许您摆脱所做的web.config更改,并在不需要时保持未分配的内存不会保持高位。

经过大量的研究,阅读和分析,我已经能够certificate我们的IIS内存使用量实际上在标准范围内; 这是通过使用SysInternals Test Limit实用程序将服务器的物理内存使用率推到接近最大值,一旦完成,我们所有的应用程序都会释放内存。

我们在测试环境中仍然存在一些我需要调查的内存问题但是在这一点上我认为我可以自信地说这完全不相关。

故事的道德是不要假设报道的问题是正确的。

您可以尝试在.NET Framework目录中的Aspnet.config文件中启用gcTrimCommitOnLowMemory设置:

启用gcTrimCommitOnLowMemory设置后,垃圾回收器将评估系统内存负载,并在负载达到90%时进入修剪模式。 它保持修剪模式,直到负载降至85%以下。

https://msdn.microsoft.com/en-us/library/bb384209(v=vs.110).aspx

另一个选项(自.net v4.5起)是在同一个Aspnet.config文件中将performanceScenario设置为“HighDensityWebHosting” 。 这对于共享托管方案很有用,因为它将“调整垃圾收集以优化内存”: http : //www.asp.net/aspnet/overview/aspnet-and-visual-studio-2012/whats-new#_Toc_perf_5

从CoreCLR源代码可以看出,HighDensityWebHosting选项主要禁用gcServergcConcurrent设置,但启用了gcTrimCommitOnLowMemory : https : //github.com/dotnet/coreclr/blob/cbf46fb0b6a0b209ed1caf4a680910b383e68cba/src/vm/perfdefaults.cpp