System.Data.SqlClient老版本引发内存溢出的问题

有一个朋友和我说他负责的项目有内存溢出的情况,内存一直居高不下达到了8G。生产环境碰到内存溢出的情况我还没有碰到过,这次有机会实践一下,准备试试,拿到了dump文件,压缩包300M,解压出来3G。该事件是发生在2023年,写这篇文件的是已经是2024年了,为什么会间隔这么久才写这篇文章,因为当时没想到要写文章记录一下,但是后面想写的时候发现dump文件不见了,最近在整理电脑资料的时候发现了这个大文件。

1. 用VS查看dump

1.1 通过VS打开dump

image

1.2 选择右侧的调试托管内存

image

image

看计数和大小以及非独占大小确实存活的对象好多,并且占用的内存也不小。

1.3 分析这些没有被GC回收的对象

image

image

image
image

通过查看前几个对象发现,最终都指向到同一个类System.Data.SqlClient.SNI.SNIMarsManager里面的一个静态变量Singleton,看到这里的时候还很疑惑,这不是微软的库嘛,有内存溢出的问题不该早解决了。

1.4 查找System.Data.SqlClient.SNI.SNIMarsManager该类的相关资料

确实在GitHub上面找到了一个说自己写一个了web api链接sqlserver并且启用了MARS会导致产生了内存但没有释放,这里的疑惑在于该问题已于2017年解决了,现在的项目怎么也不可能用2017年的版本SqlClient。

image

Memory overconsumption on SqlClient with MARS #22949

image

  1. 查看System.Data.SqlClient该库源码看看
    通过反编译工具查看已经没有SNIMarsManager这个类了。
    image

接着继续找该类的资料,后面发现.NET Core 2.0 November Update - November 14, 2017这里有一个提交记录就是和该类相关的,这个时候才反映过来了为什么这个类找不到,原来类被删了
image
通过提交记录找到了提交之前的代码,该类源码如下:

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。
image
我说你升级到最新就行了。但是这个时候奇葩的问题来了,项目找不到该库,我让他程序集和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。过了一个月再问确实是没有再出现内存溢出的问题了,当时想着问题解决了就好,并没有想着记录,后面才发现这种机会难得还是需要记录一下。

posted @   长空nice  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示