服务器模式GC似乎从不收集Gen 0堆。

10
澄清问题(tl;dr) 阅读并分析了以下所有结果后,问题似乎归结为GC在服务器模式下不收集我们应用程序的Gen 0堆,一旦将其更改为工作站模式,问题就会消失。 原问题和详细信息 我的问题与此问题此问题有些相关。
最近,在我们的测试环境中,我们的.NET应用程序出现了内存泄漏的情况。当负载较重或无负载时,工作进程的使用率会快速上升到约450MB。
在开发环境中无法复制该问题,主要区别是开发环境是物理服务器,而测试环境是虚拟化的,并由Puppet控制(除此之外,我对环境本身没有太多了解)。
为了希望看到哪些对象占用了所有内存,我在测试服务器上运行了Ants Memory Profiler,发现所有内存都保持未使用状态,从未被释放。
在研究可能导致这种情况的原因时,我发现这个论坛帖子,它又引导我找到了这篇文章
最终,我尝试了它推荐的配置,将GC置于工作站模式:
<configuration>
  <runtime>
    <gcServer enabled="false"/>
    <gcConcurrent enabled="false"/>
  </runtime>
</configuration>

运行了iisreset并重新进行了内存分析后,问题完全消失了,这很棒,但仍然无法解释最初发生了什么。
我阅读了更多资料,并找到了此SO问题,这让我相信这种配置更改可能会对我们应用程序的吞吐量产生负面影响。
所以我的问题是:是什么导致IIS工作进程累积大量未使用的内存,而这些内存从未被垃圾回收?
编辑: 为了更清楚地说明我的问题,我认为我们已经证明了代码不是造成这个问题的原因,因为在开发环境中,完全相同的代码没有遇到这个问题。
以下是我在配置更改前后拍摄的内存分析屏幕截图,这里没有太多信息,但图表很好地显示了内存趋势。

Before Configuration Change After Configuration Change

编辑2: 以下是我所能收集到的服务器规格,我可能可以获得更多的信息,但需要时间。
开发环境: 物理机器 CPU:单核 内存:6GB
测试环境: 虚拟机 CPU:4个逻辑线程(我无法评论CPU数量) 内存:8GB
机器配置文件中唯一的区别是开发环境在端点和服务行为中添加了“Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior”。
而测试环境目前在aspnet.config文件中设置了先前提到的GC设置。
编辑3: 进行了更多的性能分析,并注意到了一些可以添加到Ants的计数器,特别是我添加了“Gen 0 heap size”,看起来这是问题的根源。当我触发用于分析的测试时,使用服务器模式的GC,此行立即跳至约300MB,然后回落到约230MB,但从未完全回落(下图)。

Gen 0 Before

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

Gen 0 After

进行了更多的搜索后,我发现另一个与此相关的SO问题,但他的发现是这种内存使用并不是问题,而在我的情况下,该服务实际上需要手动重启至少一天一次。
我还发现这篇文章对这个问题有以下描述(似乎几乎完美地描述了正在发生的事情):

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

尽管问题仍然存在,在服务器模式下,第0代堆似乎永远不会被收集,而不仅仅是不那么频繁。

此外,如果您的代码没有调用dispose来释放非托管资源,则可以分配未被.Net使用的内存。例如,假设您打开了一个文件流,但没有处理文件流并且它超出了范围。GC将清理其所有.net组件,但非托管部分(Windows API中的文件句柄)仍将保持打开状态,并且Antz Profiler可能无法检测到正在使用的非托管资源。 - Ryan Mann
@Ryios,我不认为这是代码问题,原因在我提出问题的开头已经涉及,并且我在编辑中进行了澄清。 - user1618236
@Phaeze 测试环境的 CPU 数量与开发环境相同吗?这些环境中的全局 machine.config 或 web.config 中是否有任何 GC 设置? - Sebastian
@lowleveldesign 我已经添加了我可以立即获取的机器规格,以及机器配置文件中的一个差异。但是除了我谈论的gc配置之外,在测试中没有gc相关的设置,在开发中也没有任何gc相关的设置。 - user1618236
@Ryios 两个环境都具有相同的应用程序池配置,并且都是集成4.0。 - user1618236
显示剩余6条评论
3个回答

1
经过大量的研究、阅读和分析,我已经证明我们的IIS内存使用量实际上符合标准;这是通过使用SysInternals Test Limit工具将服务器的物理内存使用率推到接近最大值来完成的,一旦完成后,我们所有的应用程序都释放了它们的内存。
在我们的测试环境中仍然存在某种内存问题,我需要进行调查,但现在我可以自信地说这完全不相关。
故事的寓意是不要假设报告问题的原因是正确的。

1
您可以尝试在.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

1
“HighDensityWebHosting”设置可以在某些服务器上减少几次内存使用。在某些情况下,CPU使用率也会降低。将已使用的内存保持为稳定值可以减少Web farm中节点的数量。 - Vlad Rudenko

0

这不是直接的答案,更像是一种临时解决方法,但如果您可以在w3wp进程中运行.Net 4.5.1代码,则可以压缩LOH并减少大量未使用的分配内存[可能会]减少。

您可以创建应用程序启动代码,该代码启动定时器,每隔一段时间从w3wp.exe进程内部运行此代码。

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;

GC.Collect(); 

然而,此功能直到4.5.1版本才被添加,因此您无法在针对框架至少为4.5.1的.Net程序集中使用它。

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


我有所顧慮,因為我們有一個配置更改可以解決這個問題,我的問題是要問原因。但對於其他遇到類似問題的人來說,這絕對是值得嘗試的東西。 - user1618236
是的,我知道这不是一个答案,只是不能在评论中发布代码。如果我有答案,我会发出来的,但现在我已经没有更多的想法了,除非两个环境的操作系统不同或者生产环境正在运行超级预取,但其他人都没有回复。 - Ryan Mann
一切都好,我很感激你至少尝试了 :). 看起来我们正在开启一个微软支持工单,所以我可能很快就会有答案... 希望如此。 - user1618236

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