对 .NET FileSystemWatcher引发内存碎片化的 反思

news/2025/11/21 11:24:17/文章来源:https://www.cnblogs.com/huangxincheng/p/19251728

一:背景

1. 讲故事

前些天又遇到了一例 FileSystemWatcher 引发的内存碎片化故障,但这个碎片化不是因为经典的 reloadOnChange=true 导致的,所以我觉得有必要做一次深度的反思,供以后遇到类似问题提供技术上的解决方法,这篇我们就来系统的讲解下 两种碎片化方式的调查方法。

二:经典的 FileSystemWatcher 碎片化

1. 测试代码

这种碎片化是由 reloadOnChange=true 引发的,祸根主要是程序员将 .netframework 读取配置文件的方式套在了 .net 上,为了方便演示,先上一段测试代码。

internal class Program{static void Main(string[] args){for (int i = 0; i < 100000; i++){IConfiguration configuration = BuildConfiguration();string appName = configuration["AppName"];Console.WriteLine($"i={i} 应用名称: {appName}");}Console.ReadLine();}static IConfiguration BuildConfiguration(){return new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();}}

卦中的代码非常简单,就是每次读取 AppName 时都调了一下 BuildConfiguration 方法,仅此而已,但将程序跑起来之后,居然发现程序吃了 2.2G 的内存,真是没边的事,截图如下:

为了找出原因,上 windbg 附加,使用 !dumpheap -stat 观察托管堆,截图如下:

从卦中可以看到两点信息:

  1. Free 独占 1.39G,这是经典的内存碎片化。
  2. FileSystemWatcher 高达 1290 个,表明程序存在大量的文件监控。

看到上面两点信息,一定要有条件反射,是不是 reloadOnChange: true 导致的。

2. 是 reloadOnChange 导致的吗

要想找到答案,可以深挖 Microsoft.Extensions.Configuration.ConfigurationRoot 类,即代码 BuildConfiguration(); 的返回类型,为了方便可视化观察,我用 vs 直接找下给大家看看,截图如下:

有了这个脉络,就可以使用 windbg 下钻观察,最终就找到了 <ReloadOnChange>k__BackingField = 1 的铁证,参考如下:


0:008> !dumpobj /d 17dd2f41fa0
Name:        Microsoft.Extensions.Configuration.ConfigurationRoot
MethodTable: 00007ff9d8707a48
EEClass:     00007ff9d86e97b0
Tracked Type: false
Size:        40(0x28) bytes
File:        D:\travels\src\Example\Example_0_1\bin\Debug\net8.0\Microsoft.Extensions.Configuration.dll
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9d8706c48  4000016        8 ...on.Abstractions]]  0 instance 0000017dd2f3e520 _providers
00007ff9d880ba28  4000017       10 ...Private.CoreLib]]  0 instance 0000017dd2f42018 _changeTokenRegistrations
00007ff9d8708940  4000018       18 ...rationReloadToken  0 instance 0000017dd2f41fc8 _changeToken
0:008> !DumpObj /d 0000017dd2f3e520
Name:        System.Collections.Generic.List`1[[Microsoft.Extensions.Configuration.IConfigurationProvider, Microsoft.Extensions.Configuration.Abstractions]]
MethodTable: 00007ff9d87069d0
EEClass:     00007ff9d86a10f8
Tracked Type: false
Size:        32(0x20) bytes
File:        C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.22\System.Private.CoreLib.dll
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9d891d1a0  400226e        8     System.__Canon[]  0 instance 0000017dd2f41f68 _items
00007ff9d8551188  400226f       10         System.Int32  1 instance                1 _size
00007ff9d8551188  4002270       14         System.Int32  1 instance                1 _version
00007ff9d891d1a0  4002271        8     System.__Canon[]  0   static dynamic statics NYI                 s_emptyArray
0:008> !DumpArray /d 0000017dd2f41f68
Name:        Microsoft.Extensions.Configuration.IConfigurationProvider[]
MethodTable: 00007ff9d8707cf0
EEClass:     00007ff9d851c440
Size:        56(0x38) bytes
Array:       Rank 1, Number of elements 4, Type CLASS
Element Methodtable: 00007ff9d8706938
[0] 0000017dd2f3e540
[1] null
[2] null
[3] null
0:008> !DumpObj /d 0000017dd2f3e540
Name:        Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider
MethodTable: 00007ff9d8708200
EEClass:     00007ff9d86e9ab8
Tracked Type: false
Size:        48(0x30) bytes
File:        D:\travels\src\Example\Example_0_1\bin\Debug\net8.0\Microsoft.Extensions.Configuration.Json.dll
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9d8708940  4000012        8 ...rationReloadToken  0 instance 0000017dd2f437e0 _reloadToken
00007ff9d8708cf0  4000013       10 ...Private.CoreLib]]  0 instance 0000017dd2f42298 <Data>k__BackingField
00007ff9d8662820  4000005       18   System.IDisposable  0 instance 0000017dd2f3e690 _changeTokenRegistration
00007ff9d8701b98  4000006       20 ...nfigurationSource  0 instance 0000017dd2f3e4b8 <Source>k__BackingField
0:008> !DumpObj /d 0000017dd2f3e4b8
Name:        Microsoft.Extensions.Configuration.Json.JsonConfigurationSource
MethodTable: 00007ff9d8701c88
EEClass:     00007ff9d86e7868
Tracked Type: false
Size:        48(0x30) bytes
File:        D:\travels\src\Example\Example_0_1\bin\Debug\net8.0\Microsoft.Extensions.Configuration.Json.dll
Fields:MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9d86d8188  4000007        8 ...ers.IFileProvider  0 instance 0000017dd2f3e230 <FileProvider>k__BackingField
00007ff9d85cec08  4000008       10        System.String  0 instance 0000017d00100510 <Path>k__BackingField
00007ff9d851d070  4000009       24       System.Boolean  1 instance                0 <Optional>k__BackingField
00007ff9d851d070  400000a       25       System.Boolean  1 instance                1 <ReloadOnChange>k__BackingField
00007ff9d8551188  400000b       20         System.Int32  1 instance              250 <ReloadDelay>k__BackingField
00007ff9d8708420  400000c       18 ....FileExtensions]]  0 instance 0000000000000000 <OnLoadException>k__BackingField

三:非经典的 FileSystemWatcher 碎片化

1. 测试代码

有的时候会出现 FileSystemWatcher 很少,但 overlapped 很多的情况,这种情况很大概率不是 reloadOnChange: true 导致的,截图如下:

像这种情况可能就需要开启追踪了,可以借助🐂👃的harmony 搞定,那如何做呢?可以钩住 FileSystemWatcher 的所有构造函数,通过记录调用栈来观察到底是什么代码调用的,从而寻找祸根,参考代码如下:

internal class Program{static void Main(string[] args){var harmony = new Harmony("com.example.fswatcher");harmony.PatchAll();for (int i = 0; i < 5; i++){IConfiguration configuration = BuildConfiguration();string appName = configuration["AppName"];Console.WriteLine($"i={i} 应用名称: {appName}");}Console.ReadLine();}static IConfiguration BuildConfiguration(){return new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();}}[HarmonyPatch]public class FileSystemWatcherConstructorsPatch{[HarmonyTargetMethod]static IEnumerable<MethodBase> TargetMethods(){// 一次性获取所有公共实例构造函数return typeof(FileSystemWatcher).GetConstructors(BindingFlags.Public | BindingFlags.Instance);}[HarmonyPostfix]public static void Postfix(FileSystemWatcher __instance){Console.WriteLine($"[Harmony] FileSystemWatcher 构造函数被调用");Console.WriteLine($"[Harmony] 路径: '{__instance.Path ?? "null"}', 过滤器: '{__instance.Filter ?? "null"}'");Console.WriteLine($"[Harmony] 调用栈:");Console.WriteLine(Environment.StackTrace);}}

从卦中可以看到,原来这个 FileSystemWatcher 是我们的用户代码 BuildConfiguration 搞的哈,这就极大的缩小的包围圈,从而快速定位祸根。

四:总结

很多的内存碎片化往往都能看到 FileSystemWatcher 的身影,希望这篇的反思和总结能给大家带来帮助。

图片名称

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

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

相关文章

2025年11月合肥刑事律师推荐榜:十大专业律师权威对比与评价

随着社会法治意识不断提升,刑事案件中对专业律师的需求日益凸显。在合肥这座快速发展的城市,面对刑事案件的当事人往往处于焦虑与无助的状态,他们需要既熟悉本地司法环境又具备专业实力的律师团队。从企业高管涉及的…

女生防脱发米诺地尔哪个牌子好?蔓迪、洛健、达霏欣等多品牌分析

在对抗雄激素性脱发的道路上,米诺地尔无疑是经过临床验证的“黄金标准”。然而,传统液体剂的油腻感、丙二醇致敏风险以及2%浓度对女性效果有限等问题,常常让生发之旅充满挑战。今天,随着中国《雄激素性脱发诊疗指南…

实战分享:股票资料API接口在量化分析中的应用与体验

实战分享:股票资料API接口在量化分析中的应用与体验2025-11-21 11:17 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; dis…

2025年11月孩子留学求职规划机构推荐:一份全面解析与权威榜单

引言与现状分析 随着留学人员规模持续扩大,海外归国人员就业形势日益复杂,许多家庭开始寻求专业机构帮助子女进行职业规划。根据教育部最新统计数据,2024年我国海外留学人员总数已突破百万人,其中应届毕业生回国求…

米诺地尔哪款好用?科学防脱新选择

随着生活节奏加快与社会压力增大,脱发问题日益成为困扰现代人的“头等大事”。在众多生发产品中,米诺地尔作为经FDA认证的防脱黄金成分,已成为科学应对脱发的首选。然而,传统液体制剂油腻难吸收、丙二醇致敏等问题…

题解:GDCPC 2022 C 魔法师

题意 给定 \(n\) 个大小写字母串 \(s_i\)。每次操作可以选择两个没有选过的串 \(s_i,s_j(i\neq j)\),产生 \(\min(|\operatorname{lcp}(s_i,s_j)|,|\operatorname{lcs}(s_i,s_j))|^2\) 的贡献。求能产生的最大贡献。\…

2025 最新推荐窗帘十大品牌权威排行榜,定制 / 智能 / 遮光等全品类精选 国际协会测评认证优质品牌合集

引言 随着家居消费升级,窗帘已从单纯遮光单品升级为兼顾美学、功能与健康的核心软装。然而市场品牌繁杂,产品质量与性能差异显著,消费者难以精准抉择。本次榜单依托国际窗帘布艺协会(ICFA)最新测评体系,结合 3 大…

2025年11月岩板品牌选购指南:基于市场数据的品牌排名与性能对比

随着家居装修品质需求的提升,岩板因其耐磨、环保、设计多样化等特性,逐渐成为高端空间装饰的核心材料。2025年,岩板市场在绿色低碳政策推动下进一步规范,用户选择时更关注品牌的综合实力与交付保障。本文基于国家工…

2025 最新卫浴一线品牌推荐排行榜:权威揭晓领军品牌与新锐黑马,装修选购全攻略

引言 随着家居消费升级与二次装修需求爆发,卫浴空间已成为衡量生活品质的核心场景,消费者对产品的智能化、耐用性、环保性要求日益严苛。然而当前市场品牌鱼龙混杂,既有工艺粗糙、售后缺失的劣质产品,也有概念炒作…

2025年11月岩板品牌推荐榜单与选择指南:五大品牌综合对比分析

在当今家居装修与高端建筑装饰领域,岩板凭借其卓越的物理性能、丰富的设计表现力以及广泛的应用场景,已成为越来越多用户的首选材料。无论是追求个性化设计的高端住宅业主,还是注重实用性与美观平衡的商业空间设计师…

cad图纸怎么转换成pdf?这4个工具亲测好用!

好不容易熬夜画完的CAD图纸,发给甲方爸爸或施工队,结果对方发来一句:“你这文件我打不开啊,全是乱码!”或者是:“手机上看不了DWG,麻烦转个PDF给我。”要是有几十张CAD图纸文件需要转换,才让人真的头秃!那cad…

k8s 调试

kubectl debug -it \ rocketmq-console-67bfcb57c8-nlprv \ -n rocketmq \ --image=busybox \ --target=rocketmq-console \ -- /bin/sh “PID 看进程,Network 看网卡,IPC 看内存,/proc 是望远镜; Mount 不共享,所…

11.20 OP222操纵杆气缸报警

现象 报警操纵杆上下气缸伸出超时,有时候吸盘下降不到位,直接气缸报警。有时候其中一个吸盘没吸住,负压不足,带着老件重新去吸,叠住了。原因 1.气缸的下降速度被技师排查问题时调慢了 下图的导向轴里面有个力道不…

Linux部署Minio

Linux部署MinioMinIO 地址MinIO 是一个基于 Go语言实现的高性能对象存储。它采用AGPL(GNU Affero General Public License) 开源协议并兼容 S3 协议。官网地址:https://min.io/github地址:https://github.com/mini…

面向对象的设计第一阶段设计总结分析

前言 面向对象的设计第一阶段设计已经结束,在此时间中,我通过题目的训练,了解了许多:对一个问题进行拆解,运用对象的思路来解决问题; 熟悉了Java基本语法; 学会了代码的复用性,和相关工具的使用;第一次作业 7…

C语言中的strcat的模拟实现

strcat是字符串追加,可以在目标字符串后加上源字符串 char *strcat(char *Destination,const char *Source); 我们来模拟实现一下 #include<stdio.h> #include<assert.h> char* my_strcat(char* Destinat…

2025年比较好的真石漆岗亭厂家推荐及选择参考

2025年比较好的真石漆岗亭厂家推荐及选择参考行业背景与市场趋势随着城市化进程的加快和建筑装饰行业的持续发展,真石漆岗亭作为一种兼具美观性与实用性的建筑配套设施,近年来市场需求呈现稳定增长态势。根据中国建筑…

《数字破局》第三章需求迷雾

需求收集会开得如同菜市场。一线检验员小王希望系统能“自动填合格数据”,被李想温和但坚定地驳回:“这是为了效率牺牲原则,不行。”另一个车间的老师傅提出要系统增加“库存预警”,李想解释道:“这是WMS的领域,我…

利用配置错误的postMessage()函数实现DOM型XSS攻击

本文详细分析了postMessage函数在三种不同配置错误场景下导致的DOM型XSS漏洞,包括无源验证检查、使用indexOf()函数进行源验证以及白名单域存在XSS漏洞的情况,并提供了具体的漏洞代码和利用方法。利用配置错误的post…

《数字破局》 第二章:规划与选人

数字化是一把手工程。”文章里的话在林国栋脑中回响。他成立了项目组,自任总负责人。他力排众议,任命敢于创新的李想为项目经理,同时,他必须解决赵坤的问题。 他找赵坤深谈了一次:“老赵,我知道你担心什么。但时代…