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

时间:2015-03-23 17:32:53

标签: c# iis-7 garbage-collection

澄清问题(tl; dr)

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

原始问题和详情

我的问题与以下内容有关:This questionthis question

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

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

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

在研究导致这种情况的原因时,我遇到了this forum post,这反过来导致我this article

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

<configuration>
  <runtime>
    <gcServer enabled="false"/>
    <gcConcurrent enabled="false"/>
  </runtime>
</configuration>

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

我确实做了一些阅读并找到this SO question,这让我相信这种配置更改可能最终会对我们的应用程序的吞吐量产生不利影响。

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

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

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

Before Configuration Change After Configuration Change

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

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

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

Machine.Config文件的唯一区别是dev环境正在添加 &#34; Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior&#34;终端和服务行为。

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

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

Gen 0 Before

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

Gen 0 After

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

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

  

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

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

3 个答案:

答案 0 :(得分:1)

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

您可以创建App Start代码,启动一个定时器,该代码在w3wp.exe进程内经常运行。

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;

GC.Collect(); 

直到4.5.1才添加此功能,因此您无法在不以框架的至少4.5.1为目标的.Net程序集中使用它。

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

答案 1 :(得分:1)

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

  

启用gcTrimCommitOnLowMemory设置时,垃圾   collector评估系统内存负载并进入修剪模式   当负载达到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 选项主要禁用 gcServer gcConcurrent 设置,但启用 gcTrimCommitOnLowMemory https://github.com/dotnet/coreclr/blob/cbf46fb0b6a0b209ed1caf4a680910b383e68cba/src/vm/perfdefaults.cpp

答案 2 :(得分:0)

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

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

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