System.Data.SqlClient老版本引发内存溢出的问题
有一个朋友和我说他负责的项目有内存溢出的情况,内存一直居高不下达到了8G。生产环境碰到内存溢出的情况我还没有碰到过,这次有机会实践一下,准备试试,拿到了dump文件,压缩包300M,解压出来3G。该事件是发生在2023年,写这篇文件的是已经是2024年了,为什么会间隔这么久才写这篇文章,因为当时没想到要写文章记录一下,但是后面想写的时候发现dump文件不见了,最近在整理电脑资料的时候发现了这个大文件。
1. 用VS查看dump
1.1 通过VS打开dump
1.2 选择右侧的调试托管内存
看计数和大小以及非独占大小确实存活的对象好多,并且占用的内存也不小。
1.3 分析这些没有被GC回收的对象
通过查看前几个对象发现,最终都指向到同一个类System.Data.SqlClient.SNI.SNIMarsManager
里面的一个静态变量Singleton
,看到这里的时候还很疑惑,这不是微软的库嘛,有内存溢出的问题不该早解决了。
1.4 查找System.Data.SqlClient.SNI.SNIMarsManager
该类的相关资料
确实在GitHub上面找到了一个说自己写一个了web api链接sqlserver并且启用了MARS会导致产生了内存但没有释放,这里的疑惑在于该问题已于2017年解决了,现在的项目怎么也不可能用2017年的版本SqlClient。
Memory overconsumption on SqlClient with MARS #22949
- 查看
System.Data.SqlClient
该库源码看看
通过反编译工具查看已经没有SNIMarsManager
这个类了。
接着继续找该类的资料,后面发现.NET Core 2.0 November Update - November 14, 2017这里有一个提交记录就是和该类相关的,这个时候才反映过来了为什么这个类找不到,原来类被删了
通过提交记录找到了提交之前的代码,该类源码如下:
using System.Collections.Concurrent; using System.Collections.Generic; namespace System.Data.SqlClient.SNI { /// <summary> /// Singleton to manage all MARS connection /// </summary> internal class SNIMarsManager { public static readonly SNIMarsManager Singleton = new SNIMarsManager(); private ConcurrentDictionary<SNIHandle, SNIMarsConnection> _connections = new ConcurrentDictionary<SNIHandle, SNIMarsConnection>(); /// <summary> /// Constructor /// </summary> public SNIMarsManager() { } /// <summary> /// Create a MARS connection /// </summary> /// <param name="lowerHandle">Lower SNI handle</param> /// <returns>SNI error code</returns> public uint CreateMarsConnection(SNIHandle lowerHandle) { SNIMarsConnection connection = new SNIMarsConnection(lowerHandle); if (_connections.TryAdd(lowerHandle, connection)) { return connection.StartReceive(); } else { return TdsEnums.SNI_ERROR; } } /// <summary> /// Get a MARS connection by lower handle /// </summary> /// <param name="lowerHandle">Lower SNI handle</param> /// <returns>MARS connection</returns> public SNIMarsConnection GetConnection(SNIHandle lowerHandle) { return _connections[lowerHandle]; } } }
通过源码可以很明显的看到该类有一个静态变量Singleton,而且只有添加没有删除,这就导致MARS连接越来越多的时候内存越来越大,并且没有释放,导致程序运行占了8G,到这里基本上就找到问题的原因了,从晚上七点半一直到十一点,可以睡个好觉了。
3. 升级System.Data.SqlClient
第二天告知朋友升级System.Data.SqlClient,老版本确实存在内存泄漏的这个问题。朋友告诉我他用的库版本号是4.2。
我说你升级到最新就行了。但是这个时候奇葩的问题来了,项目找不到该库,我让他程序集和nuget引用都找找,最后定位到hangfire这个库,这是一个定时任务的库,该库可以使用sqlserver存储,有一个单独库叫Hangfire.SqlServer,在老版本安装的时候会自动安装System.Data.SqlClient,System.Data.SqlClient在Hangfire.SqlServer的依赖里面。升级一下Hangfire.SqlServer就解决了。
4. 总结
Bug出现在老版本的System.Data.SqlClient,然而ORM使用老版本的Hangfire.SqlServer的依赖库System.Data.SqlClient。过了一个月再问确实是没有再出现内存溢出的问题了,当时想着问题解决了就好,并没有想着记录,后面才发现这种机会难得还是需要记录一下。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!