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

一:背景

C# 程序内存泄漏的诱发因素有很多,但从顶层原理上来说,就是该销毁的 用户根 对象没有被销毁,从而导致内存中意料之外的对象无限堆积,导致内存暴涨,最终崩溃,这其中的一个用户根就是 终结器队列,这一篇我们就来看下如何让 PerfView 配合 WinDbg 双剑合璧。

二:如何洞察

1. 终结器内存泄漏

为了模拟 终结器内存泄漏,我们故意在 析构函数 中执行复杂的逻辑,让析构过程足够的慢,这样可以实现 分配速度 远大于 销毁速度 ,达到消费能力不足引发的内存暴涨, 参考如下代码:

internal class Program{static void Main(string[] args){Task.Run(Add);Console.ReadLine();}static void Add(){for (int i = 0; i < 1000000; i++){var person = new Person() { Name = $"jack{i}", Age = i };}}}public class Person{public string Name { get; set; }public int Age { get; set; }~Person(){Thread.Sleep(new Random().Next(0,3000));Console.WriteLine($"name={Name} 已析构了哦 ...");}}

当分配操作结束后,用 WinDbg 附加到进程中,使用 !fq 查看内存情况,输出如下:

0:015> !fq
SyncBlocks to be cleaned up: 0
Free-Threaded Interfaces to be released: 0
MTA Interfaces to be released: 0
STA Interfaces to be released: 0
----------------------------------
generation 0 has 28423 finalizable objects (000000001BF5B108->000000001BF92940)
generation 1 has 4 finalizable objects (000000001BF5B0E8->000000001BF5B108)
generation 2 has 21 finalizable objects (000000001BF5B040->000000001BF5B0E8)
Ready for finalization 971560 objects (000000001BF92940->000000001C6FC280)
Statistics for all finalizable objects (including all objects ready for finalization):MT    Count    TotalSize Class Name
00007ffdbaa2f410        4           96 System.WeakReference
00007ffdbaa4f368        2          112 System.Threading.ThreadPoolWorkQueueThreadLocals
00007ffdbaa4c640        1          168 System.Diagnostics.Tracing.FrameworkEventSource
00007ffdbaa417b8        1          168 System.Diagnostics.Tracing.NativeRuntimeEventSource
00007ffdbaa4a158        1          176 System.Threading.Tasks.TplEventSource
00007ffdbaa05650        3          216 System.Threading.Thread
00007ffdbaa240a8        1          320 System.Diagnostics.Tracing.RuntimeEventSource
00007ffdbaa24ac8        8          896 System.Diagnostics.Tracing.EventSource+OverideEventProvider
00007ffdbaa4fb58   999987     31999584 ConsoleApp2.Person
Total 1000008 objects

从上面的 Ready for finalization 971560 objects 中可以看到,当前有 9.7w 的对象正在排队等待 Finalizer 线程执行,既然它可以执行,为什么执行这么慢呢?这时候就需要调查下 Finalizer Thread 此时正在干嘛。

0:001> !t
ThreadCount:      7
UnstartedThread:  0
BackgroundThread: 6
PendingThread:    0
DeadThread:       0
Hosted Runtime:   noLock  DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception0    1     4f98 000000000058C270    2a020 Preemptive  0000000000000000:0000000000000000 00000000005814e0 1     MTA 5    2     3f4c 000000001AA94090  202b220 Preemptive  00000000088A9D60:00000000088A9FD0 00000000005814e0 0     MTA (Finalizer) 7    3     24b8 000000001AA986D0  102a220 Preemptive  0000000000000000:0000000000000000 00000000005814e0 0     MTA (Threadpool Worker) 11    4     5520 000000000056F580  1029220 Preemptive  00000000088A5100:00000000088A5FD0 00000000005814e0 0     MTA (Threadpool Worker) 12    5     1004 000000001AB26160  1029220 Preemptive  0000000000000000:0000000000000000 00000000005814e0 0     MTA (Threadpool Worker) 13    6     58a8 000000001B6D35D0    21220 Preemptive  0000000000000000:0000000000000000 00000000005814e0 0     Ukn 14    7      5b8 000000001B650820  1029220 Preemptive  0000000000000000:0000000000000000 00000000005814e0 0     MTA (Threadpool Worker) 
0:001> ~~[3f4c]s
ntdll!NtDelayExecution+0x14:
00007ffe`8908c634 c3              ret
0:005> !clrstack 
OS Thread Id: 0x3f4c (5)Child SP               IP Call Site
000000001ACEF868 00007ffe8908c634 [HelperMethodFrame: 000000001acef868] System.Threading.Thread.SleepInternal(Int32)
000000001ACEF960 00007ffe19f0c46b System.Threading.Thread.Sleep(Int32) [/_/src/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @ 259]
000000001ACEF990 00007ffdba986e15 ConsoleApp2.Person.Finalize() [D:\net6\ConsoleApp1\ConsoleApp2\Program.cs @ 31]
000000001ACEFCE0 00007ffe1a4a6c06 [DebuggerU2MCatchHandlerFrame: 000000001acefce0]

从输出中可以看到,终结器线程正在 Sleep() 函数,如果你有源码的话,可以看下 ConsoleApp2.Person.Finalize() 中的具体业务逻辑,如果没有源码的话,可以使用 !U 00007ffdba986e15 反汇编下方法源码。

0:005> !U 00007ffdba986e15
Normal JIT generated code
ConsoleApp2.Person.Finalize()
ilAddr is 00000000023920E0 pImport is 0000000002FFF460
Begin 00007FFDBA986DA0, size e9D:\net6\ConsoleApp1\ConsoleApp2\Program.cs @ 31:
00007ffd`ba986dd4 48b998b4a6bafd7f0000 mov rcx,7FFDBAA6B498h (MT: System.Random)
00007ffd`ba986dde e85d0ab25f      call    coreclr!JIT_TrialAllocSFastMP_InlineGetThread (00007ffe`1a4a7840)
00007ffd`ba986de3 488945f8        mov     qword ptr [rbp-8],rax
00007ffd`ba986de7 488b4df8        mov     rcx,qword ptr [rbp-8]
00007ffd`ba986deb e848fdffff      call    00007ffd`ba986b38 (System.Random..ctor(), mdToken: 00000000060015AB)
00007ffd`ba986df0 488b4df8        mov     rcx,qword ptr [rbp-8]
00007ffd`ba986df4 33d2            xor     edx,edx
00007ffd`ba986df6 41b8b80b0000    mov     r8d,0BB8h
00007ffd`ba986dfc 488b45f8        mov     rax,qword ptr [rbp-8]
00007ffd`ba986e00 488b00          mov     rax,qword ptr [rax]
00007ffd`ba986e03 488b4040        mov     rax,qword ptr [rax+40h]
00007ffd`ba986e07 ff5030          call    qword ptr [rax+30h]
00007ffd`ba986e0a 8945f4          mov     dword ptr [rbp-0Ch],eax
00007ffd`ba986e0d 8b4df4          mov     ecx,dword ptr [rbp-0Ch]
00007ffd`ba986e10 e833e7feff      call    00007ffd`ba975548 (System.Threading.Thread.Sleep(Int32), mdToken: 0000000006001CD5)
>>> 00007ffd`ba986e15 90              nopD:\net6\ConsoleApp1\ConsoleApp2\Program.cs @ 32:
00007ffd`ba986e16 488b0c25f0305d12 mov     rcx,qword ptr [125D30F0h] ("name=")
00007ffd`ba986e1e 48894de8        mov     qword ptr [rbp-18h],rcx
00007ffd`ba986e22 488b4d10        mov     rcx,qword ptr [rbp+10h]
00007ffd`ba986e26 e89dcfffff      call    00007ffd`ba983dc8 (ConsoleApp2.Person.get_Name(), mdToken: 0000000006000004)
00007ffd`ba986e2b 488945e0        mov     qword ptr [rbp-20h],rax
00007ffd`ba986e2f 4c8b0425f8305d12 mov     r8,qword ptr [125D30F8h] ("")
00007ffd`ba986e37 488b4de8        mov     rcx,qword ptr [rbp-18h]
00007ffd`ba986e3b 488b55e0        mov     rdx,qword ptr [rbp-20h]
00007ffd`ba986e3f e864d7feff      call    00007ffd`ba9745a8 (System.String.Concat(System.String, System.String, System.String), mdToken: 0000000006000705)
00007ffd`ba986e44 488945d8        mov     qword ptr [rbp-28h],rax
00007ffd`ba986e48 488b4dd8        mov     rcx,qword ptr [rbp-28h]
00007ffd`ba986e4c e8cf99ffff      call    00007ffd`ba980820 (System.Console.WriteLine(System.String), mdToken: 0000000006000081)
00007ffd`ba986e51 90              nop
00007ffd`ba986e52 90              nop
00007ffd`ba986e53 eb00            jmp     00007ffd`ba986e55
00007ffd`ba986e55 488bcc          mov     rcx,rsp
00007ffd`ba986e58 e808000000      call    00007ffd`ba986e65 (ConsoleApp2.Person.Finalize(), mdToken: 0000000006000008)
00007ffd`ba986e5d 90              nopD:\net6\ConsoleApp1\ConsoleApp2\Program.cs @ 33:
00007ffd`ba986e5e 90              nop
00007ffd`ba986e5f 488d6500        lea     rsp,[rbp]
00007ffd`ba986e63 5d              pop     rbp
00007ffd`ba986e64 c3              ret

最终我们找到了问题原因,在真实项目中肯定不会这么简单的,往往会执行一个复杂的逻辑,接下来我们就有一个好奇点了,那个 复杂的逻辑 会大概执行多久呢?

因为 dump 只是一个静态快照,所以从 dump 中寻找的路子就封死了,那有没有方案呢?肯定有啦,让 PerfView 大威天龙。

2. Finalize() 到底有多慢

在 CoreCLR 中有一些监控 Finalizer Thread 线程的 ETW 事件,具体是:

1)FinalizersStart 事件 2)FinalizerObject 事件 3)FinalizersStop 事件

当一个对象准备析构时,会触发 FinalizerObject ETW事件,所以观察对象之间的析构间隔,大概就能看出大致的 耗费时间

知道原理之后,接下来打开 PerfView,使用默认设置,启用 Collect -> Collect 收集,然后把应用程序跑起来,运行一段时间后,点击 Stop Collection ,在生成的 zip 面板中点击 Event ,搜索 Finalize 关键词,截图如下:

6eee262fa50a1ad81ce40d4aa204045d.png

从图中可以看到,TypeName 列都是 Person 对象,而且从 Time MSec 时间戳上可以观察到 Person 和 Person 之间相隔 s 级以上,起码说明析构函数 执行真的很慢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283321.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机的小窗口怎么弄_荣耀9X如何设置桌面小工具?划重点,这个要考

为了方便手机操作&#xff0c;一些小伙伴会在手机桌面上添加天气、一键锁屏、日历等窗口小工具&#xff0c;那么问题来了&#xff01;荣耀9X这款新手机是如何添加窗口小工具呢&#xff1f;官维君今天就来给大家讲解一下方法。标准姿势看这里——第一步&#xff1a;在桌面双指头…

ASP.NET Core 同时支持多种认证方式 | Swagger 支持

前言上次&#xff0c;我们实现了《ASP.NET Core 同时支持多种认证方式》&#xff1a;services.AddAuthentication().AddDemoAuthentication(options > { }).AddJwtBearer(options >{...});我们还希望为 Swagger 也添加多种认证支持。原来为支持 JWT 认证&#xff0c;Swag…

Redis指南

一、简介 redis 和 memcached 都是高性能的键值缓存数据库服务&#xff0c;其中 memcached 支持多线程&#xff0c;而 redis 支持丰富的数据结构且能内置持久化机制。 redis 数据都是以键值形式存储的&#xff0c;键是字符串类型&#xff0c;值有 7 种类型&#xff08;本质上是…

SpringIOC之AbstractMessageSource

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

【实战】手把手教你从 0 到 1 搭建一套 RocketMQ 集群

大家好&#xff0c;我是君哥。今天来分享怎样搭建 RocketMQ 集群。本文搭建集群使用的环境是 2 个云主机&#xff0c;架构如下&#xff1a;在 47.xx.xx.xx 和 39.xx.xx.xx 上分别部署一个 Name Server 和 Broker 主节点&#xff0c;这里不搭建从节点。也就是官网介绍的 2m-nosl…

敏捷制造:并不是你想像的矛盾体

\关键点\敏捷制造使企业能够比传统制造方式更快更有效地取悦客户 \敏捷方法实际上是为制造而不是软件而开发的 \敏捷制造需要从上到下的数字视角和承诺 \敏捷制造需要新的技能&#xff0c;还需要授权、扁平化合作和贯穿整个组织的沟通 \迈向敏捷制造的第一步可以是通过升级传统…

上海一趟,我陷入了沉思

hi&#xff0c;这里是桑小榆。2022年8月20日&#xff0c;我去了一趟上海&#xff0c;交了很多新朋友&#xff0c;和各行业且具备独立思考的伙伴交流了很多&#xff0c;也吸收了很多的新东西。此篇我将分享我的感悟&#xff0c;并以自己的角度剖析存在的问题。痛苦与症状在此之前…

Swiper(Swiper master)是目前应用较广泛的移动端网页触摸内容滑动js插件

为什么80%的码农都做不了架构师&#xff1f;>>> Swiper(Swiper master)是目前应用较广泛的移动端网页触摸内容滑动js插件 http://www.swiper.com.cn/ 转载于:https://my.oschina.net/huqiji/blog/800482

vue实现todo功能(一):搭建vue-webpack环境

前言 我最开始因为项目原因接触的是react&#xff0c;对于我这种美观狂而言&#xff0c;react中难以调解的css让我十分抓狂&#xff0c;说是在写页面&#xff0c;因为不能写自己的样式&#xff0c;像是在拼凑页面&#xff0c;没意思。于是我开始了解vue这种将css html javacrip…

单模光电转换器怎么接_以太网光纤收发器怎么用?

以太网光纤收发器有单模、多模&#xff0c;单纤、双纤&#xff0c;百兆、千兆&#xff0c;电信级和工业级品质&#xff0c;稳定可靠&#xff0c;是网络高清监控优选设备。那么&#xff0c;以太网光纤收发器怎么用呢&#xff1f;接下来就由飞畅科技的小编来为大家详细介绍下以太…

WPF效果第一百九十六篇之彩色马蹄形图

上一篇又是基于ListBox改了改模板实现了点不一样的效果;今天来分享一点这些天一直摸索的好玩的效果;闲话不多扯直接看效果:1、对于各种定义就直接看下面:https://www.wigglepixel.nl/en/blog/what-are-color-spaces-color-profiles-and-gamma-correction2、关于马蹄图我找到了S…

grub4dos中的不容易理解的问题

2019独角兽企业重金招聘Python工程师标准>>> menu.lst中写有菜单&#xff0c;但又发现很多人使用BCD,是否是这样&#xff0c;通过menu.lst中的菜单引导的系统&#xff0c;是不通过BCD文件引导的&#xff0c;还是说它们是必须同时有的&#xff0c;并且要关联呢&#…

卸载wps后桌面上的office文件图标变成了白色

文章目录卸载wps后桌面上的office文件图标变成了白色第一步&#xff1a;第二步卸载wps后桌面上的office文件图标变成了白色 作者&#xff1a;wyf 第一步&#xff1a; **win(图标&#xff09;R&#xff0c;输入regedit&#xff0c;点击确定**第二步 2、找到HKEY_CLASSES_ROOT…

springboot单例模式注入对象_Spring 中经典的 9 种设计模式,打死也要记住啊!

本文转载自公众号“Java专栏1.简单工厂(非23种设计模式中的一种)2.工厂方法3.单例模式4.适配器模式5.装饰器模式6.代理模式7.观察者模式8.策略模式9.模版方法模式Spring中涉及的设计模式总结1.简单工厂(非23种设计模式中的一种)实现方式&#xff1a;BeanFactory。Spring中的Bea…

通过项目逐步深入了解Mybatis(四)

相关阅读&#xff1a; 1、通过项目逐步深入了解Mybatis<一> 2、通过项目逐步深入了解Mybatis<二> 3、通过项目逐步深入了解Mybatis<三> 本项目所有代码及文档都托管在 Github地址&#xff1a;https://github.com/zhisheng17/mybatis 延迟加载 什么是延迟加载…

读两本敦煌书杂记-敦煌的历史(一)

前两天看了樊锦诗院长的讲座&#xff0c;八十几岁的高龄还讲的兴致盎然&#xff0c;虽然全长两个多小时但感觉一点不嫌长&#xff0c;听完深受感动。意犹未尽&#xff0c;又迫不及待的买了几本书来读。一本是《灿烂佛宫》&#xff0c;一本是《报恩父母经典故事》。因为我自己从…

在anaconda中安装tensorflow-GPU版本

在anaconda中安装tensorflow-GPU版本 第一步&#xff1a;去anaconda官网下载&#xff0c;我下载的是anaconda (python3.7版本&#xff09; anaconda下载链接&#xff1a;点击link 进去之后网页最下面有要下载的版本 安装的具体步骤&#xff1a;点击 link 第二步&#xff1a;…

云服务远程登录---设置安全组

对于小白来说购买了云服务不知道怎么开放端口和用xsell远程连接下面是步骤&#xff1a; 下面就可以玩耍了转载于:https://www.cnblogs.com/zhaojingyu/p/9021068.html

高程数据处理_珠峰长高了吗?新高程怎么算出来的?揭秘

珠穆朗玛峰是世界上海拔最高的山峰&#xff0c;被称为世界第三极&#xff0c;是亚洲的水塔。12月8日&#xff0c;珠穆朗玛峰最新高程公布&#xff0c;为8848.86米。珠峰长高了吗&#xff1f;怎么测算出来的&#xff1f;和以往的测量相比&#xff0c;“新”在哪里&#xff1f;就…

微服务框架开发(二)—扩展spring schema

2019独角兽企业重金招聘Python工程师标准>>> 一、实体bean的定义 回顾一下我们需要定义的4个标签 定义注册中心标签&#xff1a;<xmen:registry name"registry" regProtocol"zookeeper" address"127.0.0.1:2181" connectTimeout&q…