确定正在运行的垃圾收集器

12

我在运行一个大型 .NET 4.0 x86 应用程序,使用的是 Windows Server 2003 x64 (2x Xeon 4 核处理器),发现应用程序每天会出现 ~2-3 次 30 秒的冻结,然后恢复正常。应用程序每周只重新启动一次,并且消耗 400-800 MB 的内存,所以我认为这些冻结是垃圾回收导致的。我只在日志中看到这些冻结,没有在实时情况下看到过,否则我会检查任务管理器来确认。

我正在尝试找出运行的是哪个 .Net 4 GC,并确定如何将 GC 切换到新的并发后台 GC,如果它不是当前正在运行的 GC,或者如何确认这些是否确实是 GC(Procmon 在 Win2k3 服务器中不显示 .Net 工具)。


你检查了app.config/web.config中的配置了吗? - R. Martinho Fernandes
1
为什么必须进行垃圾回收?我的应用程序(占用大量内存)已经连续运行数周,而且从未出现过冻结。 - Aristos
3
你应该运行perfmon并让它收集.NET性能计数器到一个二进制日志文件中,稍后读取以查看发生了什么。 - John Saunders
有趣的内容:垃圾回收基础知识:http://msdn.microsoft.com/zh-cn/library/ee787088.aspx - Mitch Wheat
http://mitch-wheat.blogspot.com/2010/11/net-clr-large-object-heap.html - Mitch Wheat
2个回答

34

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

你可以通过以下两种方法确定你正在使用的GC版本:

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

你没有说明你正在运行何种类型的应用程序。如果你运行的是控制台应用程序、WinForm应用程序或Windows服务,则将获得工作站GC。仅仅因为你在服务器操作系统上运行,就不意味着你将获得服务器版本的GC。如果你的应用程序在多处理器机器上非托管运行,则默认会使用工作站GC - 并发方式。如果你的应用程序在多处理器机器上托管运行,则默认将使用ServerGC。

任何IIS或CLR自托管的应用程序都将默认以ServerGC模式运行。

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

工作站GC

  • 单处理器机器
  • 始终挂起线程
  • 1个短暂GC堆(SOH),1个大对象堆(LOH)GC堆
  • 运行在触发GC的线程上
  • 线程优先级与触发GC的线程相同

工作站GC - 并发方式

  • 仅在Gen2/LOH(完整收集)并发运行
  • 与服务器模式互斥
  • 工作集稍微大一些
  • 如果长时间未使用,则GC线程会过期
  • 1个短暂GC堆(SOH),1个大对象堆(LOH)GC堆
  • 专用GC线程
  • 线程优先级为Normal

服务器GC

  • 较大的段大小
  • 比工作站GC快
  • 总是暂停线程
  • 每个逻辑处理器(包括超线程)有1个临时GC堆(SOH),每个逻辑处理器(包括超线程)有1个LOH GC堆
  • 专用GC线程
  • 线程优先级为THREAD_PRIORITY_HIGHEST

无论GC模式如何,每个托管进程只有1个Finalizer线程。即使在并发GC期间,管理线程也会被暂停(阻止)两次以执行某些GC阶段。

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

在CLR 4.0中,只有一点改变

  • 并发GC现在变成了后台GC
  • 后台GC仅适用于工作站GC
  • 旧版(并发GC):
    • 在执行全局GC时允许分配到临时段大小的末尾
    • 否则,暂停所有其他线程
  • 新版(后台GC):
    • 如果必要,同时允许进行临时GC和后台GC
    • 性能更快
  • 服务器GC总是阻塞线程以收集任何代的垃圾

在CLR 4.5中,只有一点改变...再次

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

后台服务器垃圾回收(Background Server GC)

从.NET 4.5开始,所有应用程序都可以使用后台GC,不管它们使用哪个GC。后台GC是一种在多核CPU上运行的垃圾回收器,它可以在不阻止线程执行的情况下进行垃圾回收。

.NET 4.7.1 GC改进

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


真棒的答案。“此外,任何托管的CLR应用程序都将覆盖任何手动GC设置。” 这正确吗? 在单处理器机器上运行的服务器应用程序就是这样。 - davidcarr
@davidcarr 谢谢!据我所知,你是正确的。“在单处理器机器上运行的服务器应用程序”。由于存在歧义,我将澄清我的答案。 - Dave Black
虽然原帖已经发布了12年,但是您是否有关于“GC最终确定哪种模式对您的应用程序最优,并且如果它确定您的ServerGC设置会对您的应用程序产生负面影响,则将覆盖您的设置”的进一步详细信息/参考资料?感谢。 - bacar

2

如果您正在运行的是Windows服务器版本,那么默认会使用服务器版本的垃圾回收器。该版本不支持后台回收,因为垃圾是由多个线程进行收集,因此偶尔出现可观察到的暂停是很正常的。您可以通过使用app.exe.config文件来强制使用工作站版本:

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

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

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


3
仅仅因为你在“服务器”版本的Windows上运行.NET应用程序,并不意味着你会得到服务器版本的垃圾回收器。请参考我下面的答案进行解释。 - Dave Black
我只是在解释为什么他可能会观察到暂停以及他可以采取什么措施。 - Hans Passant

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