PerfView专题 (第六篇):如何洞察 C# 中 GC 的变化
一:背景
在洞察 GC 方面,我觉得市面上没有任何一款工具可以和 PerfView 相提并论,这也是为什么我会在 WinDbg 之外还要学习这么一款工具的原因,这篇我们先简单聊聊 PerfView 到底能洞察 GC 什么东西?
二:洞察 GC
1. 到底都能看到 GC 什么?
能获取到的 GC 信息非常多,比如:
程序运行期间 GC 触发了多少次?
GC 最大一次暂停耗费了多久?
每一次 GC 触发的原因是什么?
GC 暂停 >200ms 的都有哪些?
GC 触发 3 阶段中各个函数的耗时是怎样的?
等等。。。 可获取的信息非常多,后面的文章会逐一聊。
2. 获取 GC 的一般性信息
为了方面讲述,先上一段故意无限次拼接 string 的代码,让 GC 高频触发。
internal class Program
{
static void Main(string[] args)
{
Task.Run(Alloc1);
Console.ReadLine();
}
static void Alloc1()
{
var s = string.Empty;
for (int i = 0; i < 100000; i++)
{
s = s + i.ToString();
}
}
}
接下来我们用 Collect -> Run 对程序采样 20s,观察这 20s 中 GC 的触发情况。

稍等20s后,在打包好的 zip 视图列表中点击 Memory -> GCStats。

然后点击我们的应用程序,就会看到 GC 汇总信息。
GC Stats for Process 3848: ConsoleApp10
•CommandLine: "D:\net6\ConsoleApp1\ConsoleApp10\bin\Debug\net6.0\ConsoleApp10.exe"
•Runtime Version: V 6.0.522.21309 (built on 2022/4/14 1:31:32)
•CLR Startup Flags: 8388611
•Total CPU Time: 10,471 msec
•Total GC CPU Time: 1,305 msec
•Total Allocs : 38,099.086 MB
•GC CPU MSec/MB Alloc : 0.034 MSec/MB
•Total GC Pause: 2,710.6 msec
•% Time paused for Garbage Collection: 19.9%
•% CPU Time spent Garbage Collecting: 12.5%
•Max GC Heap Size: 14.285 MB
•Peak Process Working Set: 40.776 MB
•Peak Virtual Memory Usage: 5,003.682 MB
...
接下来看下 GC Rollup By Generation 列表,如下图所示:

从图中可得知这 20s 期间的如下信息:
GC触发了
1w次,平均500次/s,哈GC总的暂停时间是 2.7s,占比 13.5% ,也就是说20s时间,程序其实就跑了 86.5% 的时间,有点像 公摊面积 哈, 其实这是有问题的。
1w 次GC 中,最大的单次暂停时发生在 gen0 上,耗费了 2.6ms。
还有一个比较有用的是 Pause > 200 Msec GC Events 列表,它记录着那些暂停时间大于 >200ms 的单次GC,截图如下:

还好本次实验没有遇到,不过可以肯定的是,大于 200ms 的程序暂停肯定会导致明显的卡顿,所以仔细研读下这些 列信息 非常有用,是不是因为 存活对象过多 导致的 mark_phase 和 compact_phase 时间过长? 还是因为 pinned 或者 碎片化过多导致 plan_phase 时间过长等等,具体问题具体分析。
接下来就是这 10956 次 GC 的 detail 信息啦,可以观察到 GC 的触发原因,触发了哪一代 GC,SuppendEE 暂停时间,GC 暂停时间,GC 与上次 GC 之后的一个时间占比 等等各种信息,大家可以把鼠标放在标头上,都有相应的提示。

稍微提醒下就是 Pause Start 是没有解释的,其实它是一个毫秒时间戳,如果你仔细观察,也就是 500次/s 的排序记录。
3. 获取 GC 内部函数的 一般性耗时
熟悉 GC 的朋友应该知道,最简单的非并发工作站模式 GC 是这样的一个触发模式,如下代码所示:
GarbageCollectGeneration()
{
SuspendEE();
garbage_collect();
RestartEE();
}
garbage_collect()
{
generation_to_condemn();
gc1();
}
gc1()
{
mark_phase();
plan_phase();
}
plan_phase()
{
// actual plan phase work to decide to
// compact or not
if (compact)
{
relocate_phase();
compact_phase();
}
else
make_free_lists();
}
那如何用 PerfView 去检测这些函数的耗时呢?可以是可以,但只是一个大体的模型,因为原理是对 CPU 进行采样 再根据权重算出来的,所以有一定的参考意义。
接下来我们在 Find 输入框中搜索 coreclr!GarbageCollectGeneration 关键词。

接下来右键选择 Goto -> Goto Item in Callees 选项,然后展开可能的耗时,如下图所示:

从输出信息看, GarbageCollectGeneration 大概会耗时 12.2ms ,耗时比较长的两块大概是:
- GCScanRoots
在标记阶段,爬线程栈耗时相对较大,耗费了 3.8ms,继续往下翻的话,会发现都耗费在寻找 GC 安全点上。

- relocate_phase
这个属于 计划阶段 的重定位阶段,主要用来修改需要移动对象的新地址,所谓 兵马未动,粮草先行。

关于 GC 的洞察,还有更多好玩的东西,放在后续文章聊吧!
PerfView专题 (第六篇):如何洞察 C# 中 GC 的变化的更多相关文章
- PerfView专题 (第八篇):洞察 C# 内存泄漏之寻找静态变量名和GC模式
一:背景 这篇我们来聊一下 PerfView 在协助 WinDbg 分析 Dump 过程中的两个超实用技巧,可能会帮助我们快速定位最后的问题,主要有如下两块: 洞察内存泄漏中的静态大集合变量名. 验证 ...
- PerfView专题 (第十篇):洞察 C# 终结队列引发的内存泄漏
一:背景 C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列 ...
- PerfView专题 (第七篇):如何洞察触发 GC 的 C# 代码?
一:背景 上一篇我们聊到了如何用 PerfView 洞察 GC 的变化,但总感觉还缺了点什么? 对,就是要跟踪到底是什么代码触发了 GC,这对我们分析由于 GC 导致的 CPU 爆高有非常大的参考价值 ...
- PerfView专题 (第十一篇):使用 Diff 功能洞察 C# 内存泄漏增量
一:背景 去年 GC架构师 Maoni 在 (2021 .NET 开发者大会) [https://ke.segmentfault.com/course/1650000041122988/section ...
- PerfView专题 (第九篇):洞察 C# 中的 LOH 内存碎片化
一:背景 在 内存泄漏 的系列问题中,有一类问题是 内存碎片化 导致的,而且这种更容易发生在 LOH 上,因为它默认不开启 对象压缩,一般遇到这种情况,优先让朋友执行下面的代码应急. GCSettin ...
- PerfView专题 (第三篇):如何寻找 C# 中的 VirtualAlloc 内存泄漏
一:背景 上一篇我们聊到了如何用 PerfView 去侦察 NTHeap 的内存泄漏,这种内存泄漏往往是用 C 的 malloc 或者 C++ 的 new 分配而不释放所造成的,这一篇我们来聊一下由 ...
- PerfView专题 (第四篇):如何寻找 C# 中程序集泄漏
一:背景 前两篇我们都聊到了非托管内存泄漏,一个是 HeapAlloc ,一个是 VirtualAlloc,除了这两种泄漏之外还存在其他渠道的内存泄漏,比如程序集泄漏,这一篇我们就来聊一聊. 二: 程 ...
- PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏
一:背景 前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 PerfView 来排查 托管内存泄漏 ,其实 托管内存泄漏 比较好排查,尤其是用 WinDbg,毕竟C#是带有丰富的元数据,不像 ...
- python学习之【第十六篇】:Python中的常用模块之OS模块、sys模块、random模块
1. OS模块 OS模块是与操作系统交互的一个接口.内部提供了以下方法: os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname& ...
随机推荐
- 记一次 JDK SPI 配置不生效的问题 → 这么简单都不会,还是回家养猪吧
开心一刻 今天去幼儿园接小侄女,路上聊起了天 小侄女:小叔,今天我吃东西被老师发现了 我:老师说了什么 小侄女:她说拿出来,跟小朋友一起分享 我:那你拿出来了吗 小侄女一脸可怜的看向我,说道:没有,我 ...
- Django+Vue+Nginx+Https域名代理访问
Django+Vue使用Nginx实现Https域名的安全访问 前端 VUE 前端访问自身域名: https://demo.com,后序使用 Nginx 代理至后端 直接访问后端https:api会无 ...
- SQL Server 2017 各版本之间的差异
SQL Server 2017的亮点 您选择的语言和平台 使用您选择的语言在本地和云中(现在在 Windows.Linux 和 Docker 容器上)构建现代应用程序. 行业领先的性能 充分利用任务关 ...
- Java添加条形码到PDF表格
条码的应用已深入生活和工作的方方面面.在处理条码时,常需要和各种文档格式相结合.当需要在文档中插入.编辑或者删除条码时,可借助于一些专业的类库工具来实现.本文,以操作PDF文件为例,介绍如何在编辑表格 ...
- Spring cloud gateway 如何在路由时进行负载均衡
本文为博主原创,转载请注明出处: 1.spring cloud gateway 配置路由 在网关模块的配置文件中配置路由: spring: cloud: gateway: routes: - id: ...
- c++ 超大整数除法 高精度除法
c++ 超大整数除法 高精度除法 解题思路 计算a/b,其中a为大整数,b为普通整数,商为c,余数为r. 根据手算除法的规则,上一步的余数记为r,则本次计算的被除数为t=r*10+被除数的本位数值a[ ...
- iPhone x 的区别
最近入手两台iPhone x, 均从官网购买,两台分别是2017年和2018年生产,对比了一下,两台还有是一些差别: 首先苹果X使用起来还是非常爽的,没有HOME键,明显比按HOME键方便,因为按HO ...
- Camunda开源流程引擎快速入门——Hello World
市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda.由于jbpm.activiti.flowable这几个流程引擎出现的比较早,国内人用的 ...
- 打字练习-编程语言关键字系列-html
以下是小编整理的部分html关键字,专门给有需要的朋友进行打字练习用,通过打字练习的方式,既提高了打字速度,又可以熟悉html关键字~~~ www, url, http, W3C, html, htm ...
- 二、shell 脚本条件测试
目录 一.条件测试 test 格式 文件测试 文件测试常见选项 整数值比较 字符串比较 浮点数的运算 逻辑测试 二.if语句 1单分支 2双分支结构 3多分支结构 三元运算符 三.case 一.条件测 ...