前言

在上文[如何获取GC的STW时间]一文中,我们聊到了如何通过监听GC发出的诊断事件来计算STW时间。里面只简单的介绍了几种GC事件和它的流程。

群里就有小伙伴在问,那么GC事件是什么时候产生的?分别是代表什么含义?

那么在本文就通过几个图为大家解答一下这个问题。

有哪些GC模式?

工作站和服务器模式

在.NET中,GC其实有一些不同的工作模式,根据客户端和服务器可以分为如下两种模式:

Workstation GC

Workstation GC(工作站GC),这种模式主要是为了满足基于UI的交互式应用程序设计的,交互式意味着GC的暂停时间要尽可能的短。因为我们不想因为触发GC导致较长的GC停顿。

  • GC会更频繁的发生,每次暂停时间都会很短。
  • 内存占用率更低,因为GC更频繁的发生,所以内存回收的更积极,占用率也会更低。
  • 无论是否有配置多CPU核心,垃圾回收始终只使用一个CPU核心,只有一个托管堆。
  • 内存段的大小设置会很小。
Server GC

Server GC (服务器GC),这种模式主要是为了满足基于请求处理的WEB等类型应用程序设计的,这意味着它更侧重于需要满足大的吞吐量,零星的停顿不会对齐产生重大的影响。

  • GC的发生频率会降低,优先满足大吞吐量。
  • 内存占用率会更高,因为GC发生的频率变低,内存中可能会有很多垃圾对象。
  • 垃圾回收使用高优先级运行在多个专用线程上。每个CPU核心都提供执行垃圾回收的专用线程和堆,每个CPU核心上的堆都包含小对象、大对象堆。
  • 因为多个垃圾回收线程一起工作,所以对于相同大小的堆,Server GC会回收的更快一些。
  • 服务器垃圾回收通常会有更大的Segment,另外也会占用更多的资源。

并发与非并发模式

另外根据GC相对于用户线程的操作方式,还可以分为下面两种方式:

Non-Concurrent

Non-Concurrent(非并发GC),这种方式是一直存在于.NET中的,它适用于工作站和服务器模式,在GC进行过程中,所有的用户线程都会挂起

Concurrent(已过时)

Concurrent (并发GC),并发GC模式它和用户线程同时工作,GC进行过程中只有少数几个过程需要挂起用户线程。所以它的实现也更加复杂,但是暂停时间会更短,性能也会更好,不过现在它已经过时,本文不会着重描述它。

Background

Background(后台GC),在.NET Framework 4.0以后,后台GC取代了并发GC,它只适用于Gen2的回收,但是它可以触发对于Gen0、Gen1的回收。根据WorkstationGC和ServerGC的模式会分别在一个或多个线程上执行。

GC工作流程

需要知道的GC事件

其实对于我们分析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 //恢复托管线程运行完成

图例

为了让大家能更清晰的看懂下面的图,会用不同形状和颜色的图像来代表不同的含义,如下方所示:

绿色:正在运行的用户线程。

红色:执行引擎进行线程冻结或线程恢复。

实线箭头:正在运行的GC线程。

虚线箭头:被暂停的线程。

黄色圆球:GC事件。

红色圆球:标记点。

WorkstationGC模式-非后台(并发)GC

下图是WorkStationGC(非后台)模式的执行流程,我们假设它是在一个双核的机器上运行(下文中都是假设在双核机器上运行),运行过程其实就像下图所示。

在上图中的事件流如下所示:

  1. GC/SuspendEEStart
  2. GC/SuspendEEStop
  3. GC/Start
  4. GC/Stop
  5. GC/RestartEEStart
  6. GC/RestartEEStop

其中各个标记点分别完成了如下工作:

  • A->B:暂停所有用户线程
  • B->C: 挑选一个用户线程作为GC线程,然后开始进行垃圾回收
    • 选择-需要被回收的一代
    • 标记-被回收的一代和更年轻一代对象
    • 计划-GC决定是需要压缩整理堆还是只是清扫堆就够了
    • 清扫、搬迁和压缩-根据上面计划的结果,执行清扫堆,或者搬迁活着的对象然后整理堆,最后所有对象的地址更新到新地址。
  • C->D: GC工作结束,恢复线程运行

    由于GC暂停了所有的线程,所以A->D就是此类GC的STW Time时间。

ServerGC模式-非后台(并发)GC

下图是ServerGC(非后台)模式的执行流程。

它与WorkstationGC模式的事件流和完成的工作都一致,唯一不同的就是它会根据当前的CPU逻辑核心数量创建单独的GC线程,比如上图就有2个GC线程。

另外在服务器GC模式中,用户线程还是可以作为GC线程来使用的,像用户线程1在GC发生的时候就做了一些GC工作。

WorkstationGC模式-后台GC

下图是WorkstationGC(后台)模式的执行流程,可以看到后台模式还是相当复杂的,会短暂的暂停多次,每一次都会执行不同的操作。



除了工作线程GC以外,另外会有单独的后台GC线程进行后台垃圾回收。

上图中的事件流如下所示:

  1. GC/SuspendEEStart
  2. GC/SuspendEEStop
  3. GC/Start
  4. GC/RestartEEStart
  5. GC/RestartEEStop
  6. GC/SuspendEEStart
  7. GC/SuspendEEStop
  8. GC/RestartEEStart
  9. GC/RestartEEStop
  10. GC/SuspendEEStart
  11. GC/SuspendEEStop
  12. GC/Start
  13. GC/Stop
  14. GC/RestartEEStart
  15. GC/RestartEEStop
  16. GC/Stop

其中各个标记点完成的工作如下所示:

  • A->B:初始选择、标记

    • 此时用户线程是暂停的
    • 选择需要被回收的一代
    • 找到GC roots,以便并发标记
  • B->C:并发标记
    • 此时用户线程是正常运行的
    • 从上一步中找到的GC roots开始标记需要被回收的一代和年轻的代
  • D->E:最终标记
    • 此时用户线程是暂停的
    • 扫描在并发标记过页面,看看是否有修改让对象重新活过来的
  • F->G:清扫小对象堆
    • 此时用户线程是正常运行的
    • 清扫小对象堆的对象
  • H->I:压缩整理小对象堆、清扫压缩整理大对象堆
    • 此时用户线程是暂停的
    • 选择了一个用户线程进行GC
    • 用来压缩小对象堆的对象
    • 另外也会压缩和整理大对象堆对象
  • J->K:清扫大对象堆
    • 此时用户线程是正常运行的
    • 此时会清扫和整理大的对象堆
    • 此时会禁止分配大对象,阻塞对应线程直到大对象堆回收完成

从上面的的流程中可以看到,后台GC主要是通过并发+多次短暂暂停来实现提升吞吐量和降低总体的STW Time的,其内部实现是非常复杂的,有兴趣的小伙伴可以直接看dotnet/runtime/gc.cpp文件。

ServerGC模式-非后台GC

下图是ServerGC(后台)模式的执行流程。



它与WorkstationGC模式的事件流和完成的工作都一致,唯一不同的就是它会根据当前的CPU逻辑核心数量创建单独的GC线程,比如上图就有2个GC线程,2个后台GC线程。

总结

今天带了解了一下.NET GC中的各个阶段和事件的顺序,当然这里只是简单的带大家了解一下,要知道在任何有runtime的平台中,GC是其中相当关键的东西,大家如果对GC感兴趣,可以阅读附录中的资料。

附录

.NET GC工作流程的更多相关文章

  1. Mybatis第一篇【介绍、快速入门、工作流程】

    什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为 ...

  2. [Inside HotSpot] C1编译器工作流程及中间表示

    1. C1编译器线程 C1编译器(aka Client Compiler)的代码位于hotspot\share\c1.C1编译线程(C1 CompilerThread)会阻塞在任务队列,当发现队列有编 ...

  3. 优秀Java程序员必须了解的GC工作原理

    一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应 ...

  4. struts2工作流程

    struts2的框架结构图 工作流程 1.客户端请求一个HttpServletRequest的请求,如在浏览器中输入http://localhost: 8080/bookcode/Reg.action ...

  5. SecondaryNameNode的工作流程

    SecondaryNameNode是用来合并fsimage和edits文件来更新NameNode和metadata的. 其工作流程为: 1.secondary通知namenode切换edits文件 2 ...

  6. Storm 中什么是-acker,acker工作流程介绍

    概述 我们知道storm一个很重要的特性是它能够保证你发出的每条消息都会被完整处理, 完整处理的意思是指: 一个tuple被完全处理的意思是: 这个tuple以及由这个tuple所导致的所有的tupl ...

  7. gitlab工作流程简介

    gitlab工作流程简介 新建项目流程 创建/导入项目 可以选择导入github.bitbucket项目,也可以新建空白项目,还可以从SVN导入项目 建议选择private等级 初始化项目 1.本地克 ...

  8. Git 工作流程

    Git 作为一个源码管理系统,不可避免涉及到多人协作. 协作必须有一个规范的工作流程,让大家有效地合作,使得项目井井有条地发展下去.”工作流程”在英语里,叫做”workflow”或者”flow”,原意 ...

  9. Spark基本工作流程及YARN cluster模式原理(读书笔记)

    Spark基本工作流程及YARN cluster模式原理 转载请注明出处:http://www.cnblogs.com/BYRans/ Spark基本工作流程 相关术语解释 Spark应用程序相关的几 ...

随机推荐

  1. 使用fastai训练的一个性别识别模型

    在学习了python中的一些机器学习的相关模块后,再一次开始了深度学习之旅.不过与上次的TensorFlow框架不同,这一次接触的是fast.ai这样一个东西.这个框架还不稳定,网上也没有相关的中文文 ...

  2. Microsoft Graph 的 .NET 6 之旅

    这是一篇发布在dotnet 团队博客上由微软Graph首席软件工程师 Joao Paiva写的文章,原文地址: https://devblogs.microsoft.com/dotnet/micros ...

  3. 前端面试 -HTTP系列

    http和https 的区别? 端口 经济 安全性 响应速度 http 80端口 不需要 明文传输,安全性差 页面响应速度快,使用tcp的3次握手 https 443端口 费钱SSL需要ca 证书 S ...

  4. 聊聊FLINK-25631贡献

    从入行做数据库开发,到2018年过渡到大数据开发,可以说我已经与sql朝夕相处了七八年了,经常惊讶于简单的语法就能产生复杂的操作,而且还能根据索引等统计信息自动优化,不禁很想实现自己的sql语法,却不 ...

  5. Dapr学习(2)之Rancher2.63(k8s&k3s)环境安装Dapr

    前言:前面写过一篇关于dapr入门安装的文章,self-host模式,使用docker安装的本地调试环境,并进行了测试:本篇介绍k8s方式安装dapr,此文主要基于的环境是k3s,通过rancher2 ...

  6. 使用Go实现健壮的内存型缓存

    使用Go实现健壮的内存型缓存 本文介绍了缓存的常见使用场景.选型以及注意点,比较有价值. 译自:Implementing robust in-memory cache with Go 内存型缓存是一种 ...

  7. 我使用Spring AOP实现了用户操作日志功能

    我使用Spring AOP实现了用户操作日志功能 今天答辩完了,复盘了一下系统,发现还是有一些东西值得拿出来和大家分享一下. 需求分析 系统需要对用户的操作进行记录,方便未来溯源 首先想到的就是在每个 ...

  8. 20 HTTP 长连接与短连接

    20 HTTP 长连接与短连接 每日一句 纸上得来终觉浅,绝知此事要躬行. 每日一句 Never give up until the fight is over. 永远不要放弃,要一直战斗到最后一秒. ...

  9. Spark: 单词计数(Word Count)的MapReduce实现(Java/Python)

    1 导引 我们在博客<Hadoop: 单词计数(Word Count)的MapReduce实现 >中学习了如何用Hadoop-MapReduce实现单词计数,现在我们来看如何用Spark来 ...

  10. SPFA 最短路算法

    SPFA算法 1.什么是spfa算法? SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环.SPFA一般情况复杂度是O(m)O(m) ...