.NET 中 GC 的模式与风格

dotNET跨平台 2022-05-15 08:00

垃圾回收(GC)是托管语言必备的技术之一。GC 的性能是影响托管语言性能的关键。我们的 .NET 既能写桌面程序 (WINFROM , WPF) 又能写 web 程序 (ASP.NET CORE),甚至还能写移动端程序。。。不同使用场景的程序对 GC 的风格也有不同的要求,比如桌面程序更注重界面的响应速度,web 程序注重的是吞吐量。有幸的是 CLR 为我们提供了2种不同的 GC 模式与风格。

Workstation GC

工作站模式被设计为客户端(桌面)程序使用,或者某些只有1个核心的机器使用。工作站模式下 GC 的回收频次会加快,但是每一次 GC 造成的停顿很短暂。工作站模式的垃圾回收直接发生在触发垃圾回收的用户线程上。所以垃圾回收线程需要跟其他用户线程去竞争 CPU 时间。工作站模式下只会分配一个 GC 堆,在工作站模式下 GC 分配的内存会更少。

Server GC

服务器模式适合大型的服务端应用,比如 ASP.NET Core 程序。服务器模式下 GC 的回收会尽量的延迟,从而减少停顿。为了获得更高的吞吐量与性能,程序会分配更多的内存。服务器模式下 CLR 根据 CPU 核心数量来分配 GC 堆的数量。同时为每个 GC 堆分配一个专用线程来执行回收,并且这个线程的优先级为 THREAD_PRIORITY_HIGHEST ,所以在与普通线程竞争的时候更容易获得 CPU 时间。服务器模式通常具有更大容量的内存分段。分段容量的大小不是固定的,它跟 OS,逻辑 CPU 数量有关系:

设置使用 workstation 或者 server 模式

根据微软的文档上写的客户端单机程序默认的GC模式是 workstation ,ASP.NET 的 GC 模式取决于主机。如果不清楚默认的 GC 工作模式可以直接指定模式。
在不同的 .NET 版本下有不同的设置方式,参见下图:

我们上面说的 workstation 模式跟 server 模式是 GC 的两个主要模式。而 GC 在这两个模式之下还有两个不同的子风格 Non-Concurrent 跟 Background (concurrent) 风格。

Non-Concurrent

根据 CLR 之前的设计,如果 GC 线程启动那么所有其他的线程都会挂起。

回收 0代、1代这种短代(ephemeral generations)速度是非常快,但是回收2代垃圾对象就相对比较慢。如果线程一直挂起会对程序的响应造成比较大的影响。于是 CLR 设计了 background(concurrent) GC 。background GC 使用专用线程来回收2代对象,并且回收的时候不会挂起其他线程。

Background

在 background(concurrent) GC 回收执行的时候,0,1代的回收可以同步进行。background(concurrent) GC 使用一个或者多个专用线程来执行回收动作。在 .NET Framework 4 之前称之为 concurrent GC ,之后称之为 background GC 。在 .NET Framework 4 时代,background GC 只支持在 workstation GC 模式下开启。从 .NET Framework 4.5 开始 background GC 同时支持 workstation , server 模式。以下不再区分 background 跟 concurrent GC,统一使用 background 来描述。background GC 运行的时候并不会挂起其他线程,但是反过来如果 1,2 代的 GC 正在运行那么会挂起其他所有的线程,包括 background 专用线程。

background GC 在 workstation 跟 server 模式下有一些区别:

in workstation mode

在 workstation 模式下 background GC 使用一个专用线程。
通过上图我们可以看到:THREAD 1 发生 GC 的时候 其他线程包括 background GC 线程都会暂停。而 GC THREAD (background)启动的时候则不会挂起其他线程,而且 GC THREAD 线程只有一个 。

in server mode

在 server 模式下 background GC 会使用多个专用线程。线程的数量取决于逻辑处理器的数量。与在 workstation 模式下不同,server 模式下的 background GC 线程不会超时。

上图中 GC THREAD1、2 代表 FGC 线程,它执行的时候会挂起其他所有的线程包括 BGC 线程。图中的 BGC THREAD1、2 代表专用 background GC 线程。可以看到它执行的时候不会挂起其他线程,而且线程的数量并不是唯一的。

开启关闭 background GC

根据微软的文档说明,在 Server 模式下,background 是默认的 GC 风格。也可以直接通过配置开启 background GC 。
在不同的 .NET 版本下有不同的配置方式,参见下图:

总结

通过以上我们对 GC 的 workstation / server 模式,以及 no-concurrent 跟 background GC 风格有了一定的了解。总结一下:如果你的程序是个客户端程序需要 UI 更快的响应,希望 GC 造成的用户线程暂停时间更短那么选用 workstation 模式。如果你的电脑只有一个处理器那么也选择 workstation 模式。如果你的程序是大型 web 服务,你希望尽可能的利用服务器 CPU 与内存从而获得更大的吞吐量与性能,那么选用 server 模式。在 server 模式下我们也应该尽量使用 background GC 。因为它可以更加充分的利用的多核处理器的优势来进行 GC 操作。

参考

workstation-server-gc
wbackground-gc
fundamentals-gc
CLR 的 GC 工作模式介绍
understanding-different-gc-modes-with-concurrency-visualizer

关注我的公众号一起玩转技术


推荐阅读