一:背景

在洞察 GC 方面,我觉得市面上没有任何一款工具可以和 PerfView 相提并论,这也是为什么我会在 WinDbg 之外还要学习这么一款工具的原因,这篇我们先简单聊聊 PerfView 到底能洞察 GC 什么东西?

二:洞察 GC

1. 到底都能看到 GC 什么?

能获取到的 GC 信息非常多,比如:

  1. 程序运行期间 GC 触发了多少次?

  2. GC 最大一次暂停耗费了多久?

  3. 每一次 GC 触发的原因是什么?

  4. GC 暂停 >200ms 的都有哪些?

  5. 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 ,耗时比较长的两块大概是:

  1. GCScanRoots

在标记阶段,爬线程栈耗时相对较大,耗费了 3.8ms,继续往下翻的话,会发现都耗费在寻找 GC 安全点上。

  1. relocate_phase

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

关于 GC 的洞察,还有更多好玩的东西,放在后续文章聊吧!

PerfView专题 (第六篇):如何洞察 C# 中 GC 的变化的更多相关文章

  1. PerfView专题 (第八篇):洞察 C# 内存泄漏之寻找静态变量名和GC模式

    一:背景 这篇我们来聊一下 PerfView 在协助 WinDbg 分析 Dump 过程中的两个超实用技巧,可能会帮助我们快速定位最后的问题,主要有如下两块: 洞察内存泄漏中的静态大集合变量名. 验证 ...

  2. PerfView专题 (第十篇):洞察 C# 终结队列引发的内存泄漏

    一:背景 C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列 ...

  3. PerfView专题 (第七篇):如何洞察触发 GC 的 C# 代码?

    一:背景 上一篇我们聊到了如何用 PerfView 洞察 GC 的变化,但总感觉还缺了点什么? 对,就是要跟踪到底是什么代码触发了 GC,这对我们分析由于 GC 导致的 CPU 爆高有非常大的参考价值 ...

  4. PerfView专题 (第十一篇):使用 Diff 功能洞察 C# 内存泄漏增量

    一:背景 去年 GC架构师 Maoni 在 (2021 .NET 开发者大会) [https://ke.segmentfault.com/course/1650000041122988/section ...

  5. PerfView专题 (第九篇):洞察 C# 中的 LOH 内存碎片化

    一:背景 在 内存泄漏 的系列问题中,有一类问题是 内存碎片化 导致的,而且这种更容易发生在 LOH 上,因为它默认不开启 对象压缩,一般遇到这种情况,优先让朋友执行下面的代码应急. GCSettin ...

  6. PerfView专题 (第三篇):如何寻找 C# 中的 VirtualAlloc 内存泄漏

    一:背景 上一篇我们聊到了如何用 PerfView 去侦察 NTHeap 的内存泄漏,这种内存泄漏往往是用 C 的 malloc 或者 C++ 的 new 分配而不释放所造成的,这一篇我们来聊一下由 ...

  7. PerfView专题 (第四篇):如何寻找 C# 中程序集泄漏

    一:背景 前两篇我们都聊到了非托管内存泄漏,一个是 HeapAlloc ,一个是 VirtualAlloc,除了这两种泄漏之外还存在其他渠道的内存泄漏,比如程序集泄漏,这一篇我们就来聊一聊. 二: 程 ...

  8. PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏

    一:背景 前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 PerfView 来排查 托管内存泄漏 ,其实 托管内存泄漏 比较好排查,尤其是用 WinDbg,毕竟C#是带有丰富的元数据,不像 ...

  9. python学习之【第十六篇】:Python中的常用模块之OS模块、sys模块、random模块

    1. OS模块 OS模块是与操作系统交互的一个接口.内部提供了以下方法: os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname& ...

随机推荐

  1. 前端3JS1

    内容概要 溢出属性 定位属性 z-index JavaScript简介 变量与注释 数据类型 内容详情 溢出属性 # 文本内容超出了标签的最大范围 overflow: hidden; 接隐藏文本内容 ...

  2. CabloyJS v3.1.0支持集群及更多 🎉

    在抗疫期间,CabloyJS v3.1.0设计并开发了大量特性,并且所有相关文档已集齐.强烈建议大家试用,拍砖 特性 - 后端核心 集群: 集群现在已经成为CabloyJS的一等公民.也就是说,Cab ...

  3. 最短路之 Dijkstra 算法

    普通的 Dijkstra 这是一种运用贪心的单源最短路算法,就是求从一个节点出发,到任意一个点的最短距离 首先我们要一个图 假设要求从 1 开始的单源最短路 dis[] 表示最短路数组, vis[] ...

  4. 3.对互斥事件和条件概率的相互理解《zobol的考研概率论教程》

    tag:这篇文章没太多思考的地方,就是做个过渡 1.从条件概率来定义互斥和对立事件 2.互斥事件是独立事件吗? 3.每个样本点都可以看作是互斥事件,来重新看待条件概率 一.从条件概率来定义互斥和对立事 ...

  5. Redis 切片集群的数据倾斜分析

    Redis 中如何应对数据倾斜 什么是数据倾斜 数据量倾斜 bigkey导致倾斜 Slot分配不均衡导致倾斜 Hash Tag导致倾斜 数据访问倾斜 如何发现 Hot Key Hot Key 如何解决 ...

  6. DDos、CC攻击与防御

    DDoS介绍 DDoS是英文Distributed Denial of Service的缩写,意即"分布式拒绝服务",那么什么又是拒绝服务(Denial of Service)呢? ...

  7. Python自动化办公:27行代码实现将多个Excel表格内容批量汇总合并到一个表格

    序言 (https://jq.qq.com/?_wv=1027&k=GmeRhIX0) 老板最近越来越过分了,快下班了发给我几百个表格让我把内容合并到一个表格内去.还好我会Python,分分钟 ...

  8. POI设置列宽 自动调整列宽

    for (int i = 0; i <= totalColumn; i++) { sheet.autoSizeColumn((short)i,true); //调整列宽 } 其中totalCol ...

  9. GaussDB(for MySQL) :Partial Result Cache,通过缓存中间结果对算子进行加速

    摘要:华为云数据库高级内核技术专家详解GaussDB(for MySQL)Partial Result Cache特性,如何通过缓存中间结果对算子进行加速? 本文分享自华为云社区<GaussDB ...

  10. React技巧之中断map循环

    正文从这开始~ 总览 在React中,中断map()循环: 在数组上调用slice()方法,来得到数组的一部分. 在部分数组上调用map()方法. 遍历部分数组. export default fun ...