澄清的问题(tl;dr)
在阅读并分析了下面涵盖的所有结果后,问题似乎归结为 GC 在服务器模式下没有为我们的应用程序收集 Gen 0 堆,一旦更改为工作站模式,问题就会消失。
原始问题和详细信息
我的问题与以下内容有些相关:这个问题 and 这个问题.
最近,我们在测试环境中的 .NET 应用程序中遇到了内存泄漏问题,工作进程在负载情况下会迅速攀升至 450MB 左右,在无负载情况下会逐渐攀升至 450MB 左右。
该问题无法在我们的开发环境中复制,主要区别在于开发环境是物理服务器,而测试环境是虚拟化的并由 Puppet 控制(除此之外,我对环境本身了解不多)。
为了希望了解哪些对象占用了所有内存,我在测试服务器上运行了 Ants Memory Profiler,发现所有内存都未使用且从未被释放。
在研究可能导致此问题的原因时,我遇到了这个论坛帖子这又让我本文.
我最终尝试了它推荐的配置,将 GC 置于工作站模式:
<configuration>
<runtime>
<gcServer enabled="false"/>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>
运行 iisreset 并重新运行内存分析后,问题完全消失了,这很好,但仍然不能真正解释最初发生的情况。
我确实做了更多阅读并发现这个问题,这让我相信这种配置更改最终可能会损害我们应用程序的吞吐量。
所以我的问题是:什么会导致 IIS 工作进程积累大量未使用的内存,而这些内存永远不会被垃圾收集?
Edit:为了进一步澄清我的问题,我相信我们已经证明代码对此不负有责任,因为完全相同的代码在开发环境中不会遇到此问题。
这是我在配置更改之前和之后的内存分析的屏幕截图,这里没有太多信息,但该图确实很好地显示了内存趋势。
Edit 2:以下是我能收集到的服务器规格,我可能会得到更多,只是需要时间。
开发环境:
物理机
CPU:单核
内存:6GB
测试环境:
虚拟机
CPU:4 个逻辑线程(我无法评论 CPU 数量)
内存:8GB
Machine.Config 文件中的唯一区别是开发环境添加
“Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior”到端点和服务行为。
并且测试环境当前具有前面在aspnet.config文件中提到的GC设置。
Edit 3:进行了更多分析,发现我可以在 Ants 中添加更多计数器,特别是我添加了“Gen 0 堆大小”,看起来这就是问题的根源。当 GC 处于服务器模式时,当我触发我用于分析的测试时,这条线立即跳到 ~300MB,然后回落到 ~230MB,但永远不会一路回落(下图)。
在工作站模式下使用 GC 运行相同的分析可以看到第 0 代堆大小的初始峰值要小得多,并且在请求完成时返回到基本为零(下图)。
对此进行更多搜索使我发现another更相关的SO问题,但是他的发现是这种内存使用不是问题,而在我的情况下,该服务实际上需要每天至少手动重新启动一次。
我还发现本文关于这个问题有以下说法(这似乎几乎完美地描述了正在发生的事情:
第 0 代可能在 64 位系统上拥有大量对象,尤其是当您使用服务器垃圾收集而不是工作站垃圾收集时。这是因为在这些环境中触发第 0 代垃圾收集的阈值较高,并且第 0 代垃圾收集可能会变得更大。当应用程序在触发垃圾收集之前分配更多内存时,性能会得到提高。
尽管问题仍然存在,但在服务器模式下,第 0 代堆似乎从未被收集过,而不是经常被收集。