【问题标题】:Server mode GC seems to never collect Gen 0 Heap服务器模式 GC 似乎永远不会收集 Gen 0 Heap
【发布时间】:2015-03-23 17:32:52
【问题描述】:

澄清问题(tl;dr)

在阅读和分析下面涵盖的所有结果后,问题似乎归结为 GC 在服务器模式下没有为我们的应用程序收集 Gen 0 堆,一旦切换到工作站模式,问题就会消失。

原始问题和详细信息

我的问题有点相关: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 工作进程积累大量从未被垃圾回收的未使用内存?

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

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

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

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

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

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

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

编辑 3: 进行了更多分析并注意到我可以在 Ants 中添加更多计数器,特别是我添加了“Gen 0 堆大小”,看起来这是问题的根源。当 GC 处于服务器模式时,当我触发用于分析的测试时,这条线会立即上升到 ~300MB,然后又下降到 ~230MB,但永远不会一直下​​降(下图)。

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

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

我还发现 this article 对这个问题有以下说法(这似乎几乎完美地描述了正在发生的事情:

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

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

【问题讨论】:

  • 可能是一百万的任意数量。如果不了解您的应用程序、在其上运行的代码类型,甚至可能是代码本身,就很难说清楚。
  • 只是一个想法,如果您在应用程序池上启用了多个工作进程并且您没有设置空闲超时,那么重负载可能会启动额外的工作进程,然后它们在不处于重负载时从不关机,因为它们不会闲置。
  • 此外,如果您的代码未调用 dispose 来释放非托管资源,则可以分配 .Net 未使用的内存。例如。假设您打开了一个文件流,但没有处理该文件流并且它超出了范围。 GC 将清理它的所有 .net 组件,但非托管部分(windows api 中的文件句柄)仍将打开,并且 Antz Profiler 可能不会检测到正在使用的非托管资源。
  • @Ryios,我不认为这可能是代码问题,因为我在问题的顶部提到了一些原因,并且我在编辑中已经澄清了。
  • @Ryios,如果非托管资源泄漏,我相信我会在我的内存分析中看到这一点,我会看看我是否可以编辑我的分析截图足以发布。

标签: c# iis-7 garbage-collection


【解决方案1】:

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

我们的测试环境中确实存在某种内存问题,我需要对其进行调查,但在这一点上,我认为我可以自信地说这完全不相关。

故事的寓意是不要假设报告的问题原因是正确的。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • HighDensityWebHosting 设置可以在某些服务器上减少几次内存使用。在某些情况下,CPU 使用率也会降低。将已用内存保持为稳定值可以减少网络场中的节点数量。
    【解决方案3】:

    不是直接的答案,更像是创可贴,但如果您可以在该 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 所做的更改,并防止未分配的内存在不需要时保持高位。

    【讨论】:

    • 我很谨慎地尝试这个,因为我们有一个配置更改确实消除了这个问题,我的问题是询问原因是什么。不过,对于其他有类似问题的人来说,绝对值得一试。
    • 是的,我知道这不是答案,只是不能在 cmets 中发布代码。如果我有答案,我会发布它,但没有想法.. 除非两个环境的操作系统不同,或者 superfetch 正在生产中运行,否则我买了我剩下的所有东西,没有其他人回复。
    • 一切都很好,我很感激你至少尝试过:)。看起来我们正在打开 MS 支持票,所以我可能很快就会有答案......希望
    猜你喜欢
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 2015-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多