【问题标题】:Determining which garbage collector is running确定正在运行的垃圾收集器
【发布时间】:2011-08-04 07:54:18
【问题描述】:

我在 Windows Server 2003 x64(2x Xeon 4 核心 procs)上运行一个大型 .net 4.0 x86 应用程序,并且遇到问题,我的应用程序每天大约 2-3 次冻结 30 秒,然后恢复运行像平常一样。该应用程序每周仅重新启动一次,并消耗 400-800 mb 的内存,所以我假设这些冻结是垃圾收集。我只在日志中看到冻结,而不是实时,或者我会检查任务管理器来确认。

我试图弄清楚哪个 .Net 4 GC 正在运行,如果没有,如何将 GC 切换到新的并发后台 gc,或者如何确认这些实际上是 GC(Procmon 没有在 Win2k3 服务器中显示 .Net 工具)。

【问题讨论】:

标签: .net garbage-collection


【解决方案1】:

我已经在我的博客上重新发布了这个答案:http://dave-black.blogspot.com/2012/04/how-to-determine-which-garbage.html

您可以通过 2 种方法确定您正在运行的 GC 版本:

  1. 调用 System.Runtime.GCSettings.IsServerGC 属性
  2. 使用 WinDbg 附加到进程并使用不带引号的命令“!sos.threads”检查您有多少个 GC 线程,并且(根据以下标准)...

你没有说你有什么样的应用程序。如果您正在运行控制台应用程序、WinForm 应用程序或 Windows 服务,您将获得 Workstation GC。仅仅因为您在服务器操作系统上运行并不意味着您将获得服务器版本的 GC。如果您的应用程序未托管在多进程计算机上,则默认情况下您将获得 Workstation GC - Concurrent。如果您的应用托管在多进程计算机上,您将默认获得 ServerGC。

默认情况下,任何 IIS 或 CLR 自托管应用都将在 ServerGC 模式下运行。

以下内容适用于任何给定的 .NET 托管进程:

工作站GC

  • uni-proc 机器
  • 始终挂起线程
  • 1 个临时 GC 堆 (SOH)、1 个 LOH GC 堆
  • 在触发 GC 的线程上运行
  • 线程优先级与触发GC的线程相同

工作站 GC - 并发

  • 仅在 Gen2/LOH 中并发运行(完整集合)
  • 与服务器模式互斥
  • 工作集稍大
  • 如果一段时间后不使用,GC 线程将过期
  • 1 个临时 GC 堆 (SOH)、1 个 LOH GC 堆
  • 专用 GC 线程
  • 线程优先级正常

服务器垃圾回收

  • 更大的段大小
  • 比工作站 GC 更快
  • 始终挂起线程
  • 每个逻辑处理器(包括超线程)1 个临时 GC 堆 (SOH),每个逻辑处理器(包括超线程)1 个 LOH GC 堆
  • 专用 GC 线程
  • 线程优先级为 THREAD_PRIORITY_HIGHEST

无论 GC 模式如何,每个托管进程只有 1 个终结器线程。即使在并发 GC 期间,托管线程也会被挂起(阻塞)两次以执行 GC 的某些阶段。

一个鲜为人知的事实是,即使您尝试设置 GC 的 Server 模式,您也可能不会在 Server GC 中运行; GC 最终确定哪种模式最适合您的应用程序,如果它确定您的 ServerGC 设置将对您的应用程序产生负面影响,它将覆盖您的设置。此外,在单处理器计算机上运行的任何托管 CLR 应用程序都将覆盖任何手动 GC 设置 - 在这种情况下,CLR 将始终使用工作站 GC 模式。

在 CLR 4.0 中,只有一点点变化

  • 并发 GC 现在是后台 GC
  • 后台 GC 仅适用于工作站 GC
  • 旧(并发 GC):
    • 在 Full GC 期间允许分配直到临时段大小结束
    • 否则,暂停所有其他线程
  • 新(后台 GC):
    • 如有必要,允许临时 GC 与后台 GC 同时进行
    • 性能更快
  • 服务器 GC 总是阻塞线程以收集任何代

在 CLR 4.5 中,事情只发生了一点点变化......再次

  • 后台服务器 GC:
    • 服务器 GC 不再阻塞。相反,它使用专用的后台 GC 可以与用户代码同时运行的线程 - 请参阅 MSDN: Background Server GC

因此,在 .NET 4.5+ 中,所有应用程序现在都有后台 GC可用,无论它们使用哪种 GC。

.NET 4.7.1 GC 改进

.NET Framework 4.7.1 对垃圾收集 (GC) 进行了更改,以提高分配性能,尤其是对于大型对象堆 (LOH) 分配。这是由于架构更改将堆的分配锁拆分为 2 个,用于小对象堆 (SOH) 和 LOH。进行大量 LOH 分配的应用程序应该会看到分配锁争用的减少,并看到更好的性能。这些改进允许在后台 GC (BGC) 扫描 SOH 时分配 LOH。通常 LOH 分配器会等待 BGC 扫描过程的整个持续时间,然后才能满足分配内存的请求。这可能会阻碍性能。您可以在 PerfView 的 GCStats 中观察到这个问题,其中有一个“LOH 分配暂停(由于后台 GC)> 200 毫秒事件”表。暂停原因是“等待 BGC 线程空闲列表”。此功能应有助于缓解此问题。

【讨论】:

  • 很棒的答案。 “此外,任何托管的 CLR 应用程序都将覆盖任何手动 GC 设置。”这是正确的吗?在单处理器机器上运行的服务器应用程序几乎就是这样
  • @davidcarr 谢谢! AFAIK,你是对的。 “在单处理器机器上运行的服务器应用程序”。由于存在歧义,我将澄清我的答案。
【解决方案2】:

您在服务器版本的 Windows 上运行,默认情况下您将获得服务器版本的垃圾收集器。它不进行后台收集,垃圾由多个线程收集,因此偶尔可观察到的暂停并不罕见。您可以使用 app.exe.config 文件强制工作站版本:

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

还要查看 GC.RegisterForFullGCNotification() 方法的文档,了解如何处理暂停的副作用。

.NET 4.5 版将支持服务器 GC 的后台收集。

【讨论】:

  • 仅仅因为您在“服务器”版本的 Windows 上运行 .NET 应用程序并不意味着您将获得服务器版本的 GC。请参阅下面我的回答以获得解释。
  • 我只是在解释为什么他可能会观察到停顿以及他能做些什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-02
  • 2011-05-14
相关资源
最近更新 更多