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& ...
随机推荐
- 是时候为Spring Boot 3.0做准备了
2018年2月28日Spring Boot进入2.0时代,距今已经超过4年了. 2022 年 11 月 Spring Boot 3.0 将正式发布,它将基于 Spring Framework 6.0, ...
- JavaScript Object学习笔记一
Object.assign(target, source1, source2, ...)//用于对象的复制合并(同名属性后覆盖前)或拷贝(拷贝自身可枚举属性,不拷贝继承属性或不可枚举属性),将sour ...
- Spring Security整合企业微信的扫码登录,企微的API震惊到我了
本文代码: https://gitee.com/felord/spring-security-oauth2-tutorial/tree/wwopen/ 现在很多企业都接入了企业微信,作为私域社群工具, ...
- 【Java面试】@Resource 和 @Autowired 的区别
Hi,大家好,我是Mic. 一个工作2年的粉丝,问我一个Spring里面的问题. 希望我能从不同的视角去分析,然后碾压面试官. 这个问题是: "@Resource和@Autowired&qu ...
- uniapp使用scroll-view与swiper组件实现tab滑动切换页面需要注意的问题
效果图: tab栏可以滑动,切换页面跟随tab栏同步滑动.这里需要注意的是使用swiper组件时,它会有一个默认的高度,你必须动态的获取数据列表的高度覆盖原来的默认高度. 下面是代码 html < ...
- Elasticsearch学习系列四(聚合搜索)
聚合分析 聚合分析是数据库中重要的功能特性,完成对一个查询的集中数据的聚合计算.如:最大值.最小值.求和.平均值等等.对一个数据集求和,算最大最小值等等,在ES中称为指标聚合,而对数据做类似关系型数据 ...
- java: 程序包org.springframework.boot不存在
如果你的settings中的maven配置没问题的话,尝试下面这个 在控制台输入 mvn idea:idea 重构一下
- 查看mysql安装目录、安装路径
show variables like "%CHAR%" 我是在Navicat中输入这个命令后查的.
- 华为HMS Core携手超图为三维GIS注入新动能
6月30日,在2022(第五届)GIS软件技术大会GIS基础软件新技术板块论坛上,华为联合超图推出了基于HMS Core 3D Engine开发的高保真三维GIS插件,通过3D渲染技术助力三维GIS实 ...
- # NC20860 兔子的区间密码
NC20860 兔子的区间密码 题目 题目描述 有一只可爱的兔子被困在了密室了,密室里有两个数字,还有一行字: 只有解开密码,才能够出去. 可爱的兔子摸索了好久,发现密室里的两个数字是表示的是一个区间 ...