.NET 中 GC 的模式与风格

垃圾回收(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 数量有关系:

.NET 中 GC 的模式与风格

设置使用 workstation 或者 server 模式

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

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

Non-Concurrent

根据 CLR 之前的设计,如果 GC 线程启动那么所有其他的线程都会挂起。
.NET 中 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 使用一个专用线程。
.NET 中 GC 的模式与风格
通过上图我们可以看到:THREAD 1 发生 GC 的时候 其他线程包括 background GC 线程都会暂停。而 GC THREAD (background)启动的时候则不会挂起其他线程,而且 GC THREAD 线程只有一个 。

in server mode

在 server 模式下 background GC 会使用多个专用线程。线程的数量取决于逻辑处理器的数量。与在 workstation 模式下不同,server 模式下的 background GC 线程不会超时。
.NET 中 GC 的模式与风格
上图中 GC THREAD1、2 代表 FGC 线程,它执行的时候会挂起其他所有的线程包括 BGC 线程。图中的 BGC THREAD1、2 代表专用 background GC 线程。可以看到它执行的时候不会挂起其他线程,而且线程的数量并不是唯一的。

开启关闭 background GC

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

总结

通过以上我们对 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

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

.NET 中 GC 的模式与风格

发表评论

相关文章