当前位置: 首页 > news >正文

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

一:背景

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.jsqmd.com/news/46401/

相关文章:

  • 2025年11月合肥刑事律师推荐榜:十大专业律师权威对比与评价
  • 女生防脱发米诺地尔哪个牌子好?蔓迪、洛健、达霏欣等多品牌分析
  • 实战分享:股票资料API接口在量化分析中的应用与体验
  • 2025年11月孩子留学求职规划机构推荐:一份全面解析与权威榜单
  • 米诺地尔哪款好用?科学防脱新选择
  • 题解:GDCPC 2022 C 魔法师
  • 2025 最新推荐窗帘十大品牌权威排行榜,定制 / 智能 / 遮光等全品类精选 国际协会测评认证优质品牌合集
  • 2025年11月岩板品牌选购指南:基于市场数据的品牌排名与性能对比
  • 2025 最新卫浴一线品牌推荐排行榜:权威揭晓领军品牌与新锐黑马,装修选购全攻略
  • 2025年11月岩板品牌推荐榜单与选择指南:五大品牌综合对比分析
  • cad图纸怎么转换成pdf?这4个工具亲测好用!
  • k8s 调试
  • 11.20 OP222操纵杆气缸报警
  • Linux部署Minio
  • 面向对象的设计第一阶段设计总结分析
  • C语言中的strcat的模拟实现
  • 2025年比较好的真石漆岗亭厂家推荐及选择参考
  • 《数字破局》第三章需求迷雾
  • 利用配置错误的postMessage()函数实现DOM型XSS攻击
  • 《数字破局》 第二章:规划与选人
  • 2025年北京除甲醛服务机构权威评测:氧道净醛水漆/甲醛净化/新房装修除甲醛服务机构解析
  • 2025年口碑好的矿用气动遥控平板车杭州别墅大宅装修
  • 2025 年试验箱生产厂家全景推荐!六大实力厂商覆盖全品类需求,品质与服务双保障
  • 2025年靠谱的纸箱珍珠棉用户好评厂家排行
  • 2025年质量好的矿用防爆柴油机搬运车行业内口碑厂家排行榜
  • if __name__ == __main__作用
  • 2025B2B外贸独立站优化服务商有哪些-外贸服务商测评推荐
  • 2025年质量好的自动伸缩门厂家推荐及选择参考
  • 全新AI增强Demo发布:DHTMLX Gantt与Diagram如何通过LLM更智能地构建项目与组织结构
  • DELL服务器设置来电自动启动