一:背景

写这一篇的目的主要是因为.NET领域内几本关于阐述GC方面的书,都是纯理论,所以懂得人自然懂,不懂得人也没法亲自验证,这一篇我就用 windbg + 源码 让大家眼见为实。

二:为什么要引入后台GC

1. 后台GC到底解决了什么问题

解决什么问题得先说有什么问题,我们知道 阻塞版GC 有一个显著得特点就是,在 GC 触发期间,所有的用户线程都被 暂停了,这里的 暂停 是一个统称,画图如下:

这种 STW(Stop The World) 模式相信大家都习以为常了,但这里有一个很大的问题,不管当前 GC 是临时代还是全量,还是压缩或者标记,all in 全冻结,这种简单粗暴的做法肯定是不可取的,也是 后台GC 引入的先决条件。

那 后台GC 到底解决了什么问题?

解决在 FullGC 模式下的 标记清除 回收期间,放飞用户线程。

虽然这是一个很好的 Idea,但复杂度绝对上了几个档次。

三:后台GC 详解

1. 后台 GC代码 骨架图

源码面前,了无秘密,在coreclr 项目的 garbage-collection.md 文件中,描述了 后台GC 的代码流程图。

     GarbageCollectGeneration(){SuspendEE();garbage_collect();RestartEE();}garbage_collect(){generation_to_condemn();// decide to do a background GC// wake up the background GC thread to do the workdo_background_gc();}do_background_gc(){init_background_gc();start_c_gc ();//wait until restarted by the BGC.wait_to_proceed();}bgc_thread_function(){while (1){// wait on an event// wake upgc1();}}gc1(){background_mark_phase();background_sweep();}

可以清楚的看到就是在做 标记清除 且核心逻辑都在 background_mark_phase() 函数中,实现了标记的三个阶段: 1.初始标记, 2.并发标记 ,3.最终标记 , 其中 并发标记 阶段,用户线程是正常运行的,实现了将原来整个暂停 优化到了 2个小暂停。

2. 流程图分析

为了方便说明,将三阶段画个图如下:

特别声明:阶段2的重启是在 background_sweep() 方法中,而不是 最终标记(background_mark_phase) 阶段。

  1. 初始标记

这个阶段用户线程处于暂停状态,bgc 要做的事情就是从 线程栈 和 终结器队列 中寻找用户根实现引用图遍历,然后再让所有用户线程启动,简化后的代码如下:

void gc_heap::background_mark_phase()
{dprintf(3, ("BGC: stack marking"));GCScan::GcScanRoots(background_promote_callback,max_generation, max_generation,&sc);dprintf(3, ("BGC: finalization marking"));finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);restart_vm();
}

接下来怎么验证 阶段1 是暂停状态呢?为了方便讲述,先上一段测试代码:

    internal class Program{static List<string> list = new List<string>();static void Main(string[] args){Debugger.Break();for (int i = 0; i < int.MaxValue; i++){list.Add(String.Join(",", Enumerable.Range(0, 100)));if (i % 10 == 0) list.RemoveAt(0);}}}

然后用 windbg 在 background_mark_phase 函数下一个断点:bp coreclr!WKS::gc_heap::background_mark_phase 即可。

0:009> bp coreclr!WKS::gc_heap::background_mark_phase
0:009> g
Breakpoint 1 hit
coreclr!WKS::gc_heap::background_mark_phase:
00007ff9`e7bf73f4 488bc4          mov     rax,rsp
0:008> !t -specialLock  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1     55d8 00000000006336B0    2a020 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 MTA (GC) 6    2     568c 0000000000662F40    21220 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 Ukn (Finalizer) 8    4     5730 0000000000676A90    21220 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 Ukn OSID Special thread type0 55d8 SuspendEE 5 5688 DbgHelper 6 568c Finalizer 8 5730 GC 

可以清楚的看到,0号线程显示了 SuspendEE 字样,表示此时所有托管线程处于冻结状态。

  1. 并发标记

这个阶段就是各玩各的,用户线程在正常执行,bgc在后台进一步标记,因为是并行,所以存在 bgc 已标记好的对象引用关系被 用户线程 破坏,所以 bgc 用 reset_write_watch 函数借助 windows 的内存页监控,目的就是把那些脏页找出来,在下一个阶段来修正,简化后的代码如下:

void gc_heap::background_mark_phase()
{disable_preemptive(true);//脏页监控reset_write_watch(TRUE);revisit_written_pages(TRUE, TRUE);dprintf(3, ("BGC: handle table marking"));GCScan::GcScanHandles(background_promote,max_generation, max_generation,&sc);disable_preemptive(false);
}

要想验证此时的用户线程是放飞的,可以在 revisit_written_pages 函数下一个断点即可,使用命令:bp coreclr!WKS::gc_heap::revisit_written_pages 。

0:008> !t -specialLock  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1     55d8 00000000006336B0    2a020 Cooperative 000000000D1FD920:000000000D1FE120 000000000062d650 -00001 MTA 6    2     568c 0000000000662F40    21220 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 Ukn (Finalizer) 8    4     5730 0000000000676A90    21220 Cooperative 0000000000000000:0000000000000000 000000000062d650 -00001 Ukn OSID Special thread type5 5688 DbgHelper 6 568c Finalizer 8 5730 GC 

看到没有,那个 SuspendEE 神奇的消失了,而且 0 号线程的 GC 模式也改成了 Cooperative,表示可允许操控 托管堆。

  1. 最终标记

等 bgc 在后台做的差不多了,就可以再来一次 SupendEE,将 并发标记 期间由用户线程造成的脏引用进行最终一次修正,修正的数据来源就是监控到的 Windows脏页,代码就不上了,我们聊下怎么去验证阶段二又回到了 SuspendEE 状态?可以在 background_sweep() 函数下一个断点, 命令: bp coreclr!WKS::gc_heap::background_sweep 。

0:000> bp coreclr!WKS::gc_heap::background_sweep
0:000> g
coreclr!WKS::gc_heap::background_sweep:
00007ff9`e7b7a2e0 4053            push    rbx
0:008> !t -specialLock  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1     55d8 00000000006336B0    2a020 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 MTA 6    2     568c 0000000000662F40    21220 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 Ukn (Finalizer) 8    4     5730 0000000000676A90    21220 Preemptive  0000000000000000:0000000000000000 000000000062d650 -00001 Ukn (GC) OSID Special thread type5 5688 DbgHelper 6 568c Finalizer 8 5730 GC SuspendEE 

哈哈,可以看到那个 SuspendEE 又回来了。

3. 后台GC 只会在 fullGC 模式下吗?

这是最后一个要让大家眼见为实的问题,在gc触发期间,内部会维护一个 gc_mechanisms 结构体,其中就记录了当前 GC 触发的种种信息,可以用 windbg 把它导出来看看便知。

0:008> x coreclr!*settings*
00007ff9`e7f82e90 coreclr!WKS::gc_heap::settings = class WKS::gc_mechanisms
0:008> dt coreclr!WKS::gc_heap::settings 00007ff9`e7f82e90+0x000 gc_index         : 0xb3+0x008 condemned_generation : 0n2+0x00c promotion        : 0n1+0x010 compaction       : 0n0+0x014 loh_compaction   : 0n0+0x018 heap_expansion   : 0n0+0x01c concurrent       : 1+0x020 demotion         : 0n0+0x024 card_bundles     : 0n1+0x028 gen0_reduction_count : 0n0+0x02c should_lock_elevation : 0n0+0x030 elevation_locked_count : 0n0+0x034 elevation_reduced : 0n0+0x038 minimal_gc       : 0n0+0x03c reason           : 0 ( reason_alloc_soh )+0x040 pause_mode       : 1 ( pause_interactive )+0x044 found_finalizers : 0n1+0x048 background_p     : 0n0+0x04c b_state          : 0 ( bgc_not_in_process )+0x050 allocations_allowed : 0n1+0x054 stress_induced   : 0n0+0x058 entry_memory_load : 0x49+0x060 entry_available_physical_mem : 0x00000001`0a50d000+0x068 exit_memory_load : 0

从 condemned_generation=2 可知当前触发的是 2 代GC,原因是代满了 reason : 0 ( reason_alloc_soh ) 。

四:总结

看的再多还不如实操一遍,如果觉得手工编译 coreclr 源码麻烦,可以考虑下 windbg,好了,本篇就聊这么多,希望对你有帮助。

聊一聊 C# 后台GC 到底是怎么回事?相关推荐

  1. C# 后台GC 的前因后果

    一:背景 写这一篇的目的主要是因为.NET领域内几本关于阐述GC方面的书,都是纯理论,所以懂得人自然懂,不懂得人也没法亲自验证,这一篇我就用 windbg + 源码 让大家眼见为实. 二:为什么要引入 ...

  2. dakai微信小程序 ios_【iOS】微信小程序打开APP到底是怎么回事?

    前言 从苹果官方来看,小程序新增了两个功能: 1. 支持打开移动应用 2. 标题栏区域开放自定义 针对第二个功能,就是开发者可以自定义小程序菜单栏的颜色风格,根据需求,对小程序菜单外的标题区域进行自定 ...

  3. 设置文件权限位时我们一般忽略了suid/guid的存在,现在看看它们到底是怎么回事

    设置文件权限位时我们一般忽略了suid/guid的存在,现在看看它们到底是怎么回事. suid/guid是什么? suid意味着如果A用户对属于他自己的shell脚本文件设置了这种权限,那么其他用户在 ...

  4. c语言列车调度 栈,这个用栈调度火车的程序到底是怎么回事?

    这个用栈调度火车的程序到底是怎么回事? 下面是一个用栈来实现火车调度的程序,其中,in代表初始的车列顺序,transter代表用来调度的火车轨,out代表调度后的火车轨,程序功能如下,如果in中车列顺 ...

  5. CAS到底是怎么回事

    CAS到底是怎么回事 为什么需要CAS 如何实现CAS 关于CAS和ABA 关于应用层的锁和CPU的锁的关系 参考 为什么需要CAS CAS全称为Compare And Set(比较并交换) 对于现代 ...

  6. 大学计算机怎么开机,电脑开机卡logo,大学生没电脑玩急坏了,这到底是怎么回事?...

    电脑常见的故障有蓝屏白字.黑屏白字.红屏白字.卡logo.打不开程序.自动关机重启,这些故障有的是硬件问题引起,有的是软件问题引起,对于硬件引起的故障,我们首先要判断是接触不良引起的故障,还是因为损坏 ...

  7. 【定位不准的烦心事系列】第2篇:卫星信号弱到底是咋回事

    对于每个使用手机导航App的用户来说,最怕听到的就是"卫星导航信号弱"这个提示,因为这意味着定位不准了,用户可能无法获得准确的指引.那么信号弱到底是咋回事呢?明明没有遮挡,咋就收不 ...

  8. 【售票员内部资料】告诉你所谓的“提前10天售火车票”到底是怎么回事

    [售票员内部资料]告诉你所谓的"提前10天售火车票"到底是怎么回事    我们可能只知道"提前10天售火车票"这个信息,也就是说,火车票只在开车前10天放一次票 ...

  9. 深入理解 Java 之 GC 到底如何工作

     当程序创建对象,数组等引用类型实体时,系统都会在堆内存中为之分配一块内存区,对象就保存在这块内存区,当这块内存不再被任何变量引用时,这块内存就变成垃圾,系统就要回收. 只回收堆内存中对象,不会回 ...

最新文章

  1. url+用户名+密码_URI和URL详解
  2. 语音情感识别----语音特征集之eGeMAPS,ComParE,09IS,BoAW
  3. map集合遍历_java---map集合获取元素与存储元素
  4. FFmpegh.264解码
  5. python 曲面_Python之OpenGL笔记(35):曲面物体的构建
  6. 小米3c虚拟服务器,小米路由器3C固件逆向与测评-新手向
  7. EP100底层封装-LibCreateSourceFile()函数
  8. java图形界面的实验代码_GUI图形用户界面实验报告
  9. OJ 1478 机器翻译
  10. C# Parellel.For 和 Parallel.ForEach
  11. 修改 win 快捷键适配 macOS
  12. transPlotR 优雅的绘制基因转录本结构
  13. 互联网公司,产品和测试的那些人和事
  14. mysql怎么增加内存_MySQL内存不足怎么办
  15. 【iOS】—— FMDB的基础用法
  16. 数字信号处理-3-函数的正交
  17. 如何预防AV终结者病毒
  18. JavaScript函数及应用
  19. python 在险价值_Python计算股票投资组合的风险价值(VaR)
  20. DM365 dvsdk_4_02_00_06开发环境搭建

热门文章

  1. 隐马尔科夫模型(HMM)模型训练:Baum-Welch算法
  2. 配置环境变量path
  3. Xshell密钥认证
  4. 微商小白如何有效快速精准引流?哪里能找到精准顾客群体?
  5. BAT批处理文本替换
  6. 打开计算机用户策略,打开组策略,详细教您电脑组策略怎么打开
  7. hu沪江计算机词汇,英文字母_计算机词汇(R-Z)_沪江英语
  8. Linux攻关之基础模块一 系统准备
  9. VUE进行前后端交互
  10. ping请求超时的解决方法?