在上文[如何获取GC的STW时间]一文中,我们聊到了如何通过监听GC发出的诊断事件来计算STW时间。里面只简单的介绍了几种GC事件和它的流程。群里就有小伙伴在问,那么GC事件是什么时候产生的?分别是代表什么含义?那么在本文就通过几个图为大家解答一下这个问题。
在.NET中,GC其实有一些不同的工作模式,根据客户端和服务器可以分为如下两种模式:
Workstation GC(工作站GC),这种模式主要是为了满足基于UI的交互式应用程序设计的,交互式意味着GC的暂停时间要尽可能的短。因为我们不想因为触发GC导致较长的GC停顿。
Server GC (服务器GC),这种模式主要是为了满足基于请求处理的WEB等类型应用程序设计的,这意味着它更侧重于需要满足大的吞吐量,零星的停顿不会对齐产生重大的影响。
另外根据GC相对于用户线程的操作方式,还可以分为下面两种方式:
Non-Concurrent(非并发GC),这种方式是一直存在于.NET中的,它适用于工作站和服务器模式,在GC进行过程中,所有的用户线程都会挂起。
Concurrent (并发GC),并发GC模式它和用户线程同时工作,GC进行过程中只有少数几个过程需要挂起用户线程。所以它的实现也更加复杂,但是暂停时间会更短,性能也会更好,不过现在它已经过时,本文不会着重描述它。
Background(后台GC),在.NET Framework 4.0以后,后台GC取代了并发GC,它只适用于Gen2的回收,但是它可以触发对于Gen0、Gen1的回收。根据WorkstationGC和ServerGC的模式会分别在一个或多个线程上执行。
其实对于我们分析GC的工作来说,上文中提到的几个事件已经足够使用了,让我们再来回顾一下这些事件。
Microsoft-Windows-DotNETRuntime/GC/SuspendEEStart //开始暂停托管线程运行
Microsoft-Windows-DotNETRuntime/GC/SuspendEEStop //暂停托管线程完成
Microsoft-Windows-DotNETRuntime/GC/Start // GC开始回收
Microsoft-Windows-DotNETRuntime/GC/Stop // GC回收结束
Microsoft-Windows-DotNETRuntime/GC/RestartEEStart //恢复之前暂停的托管线程
Microsoft-Windows-DotNETRuntime/GC/RestartEEStop //恢复托管线程运行完成
为了让大家能更清晰的看懂下面的图,会用不同形状和颜色的图像来代表不同的含义,如下方所示:
配图-图例.drawio
绿色:正在运行的用户线程。红色:执行引擎进行线程冻结或线程恢复。实线箭头:正在运行的GC线程。虚线箭头:被暂停的线程。黄色圆球:GC事件。红色圆球:标记点。
下图是WorkStationGC(非后台)模式的执行流程,我们假设它是在一个双核的机器上运行(下文中都是假设在双核机器上运行),运行过程其实就像下图所示。
配图-工作站.drawio
在上图中的事件流如下所示:
其中各个标记点分别完成了如下工作:
下图是ServerGC(非后台)模式的执行流程。
它与WorkstationGC模式的事件流和完成的工作都一致,唯一不同的就是它会根据当前的CPU逻辑核心数量创建单独的GC线程,比如上图就有2个GC线程。另外在服务器GC模式中,用户线程还是可以作为GC线程来使用的,像用户线程1在GC发生的时候就做了一些GC工作。
下图是WorkstationGC(后台)模式的执行流程,可以看到后台模式还是相当复杂的,会短暂的暂停多次,每一次都会执行不同的操作。
除了工作线程GC以外,另外会有单独的后台GC线程进行后台垃圾回收。上图中的事件流如下所示:
其中各个标记点完成的工作如下所示:
从上面的的流程中可以看到,后台GC主要是通过并发+多次短暂暂停来实现提升吞吐量和降低总体的STW Time的,其内部实现是非常复杂的,有兴趣的小伙伴可以直接看dotnet/runtime/gc.cpp文件。
下图是ServerGC(后台)模式的执行流程。
它与WorkstationGC模式的事件流和完成的工作都一致,唯一不同的就是它会根据当前的CPU逻辑核心数量创建单独的GC线程,比如上图就有2个GC线程,2个后台GC线程。
今天带了解了一下.NET GC中的各个阶段和事件的顺序,当然这里只是简单的带大家了解一下,要知道在任何有runtime的平台中,GC是其中相当关键的东西,大家如果对GC感兴趣,可以阅读附录中的资料。