有一个朋友和我说他负责的项目有内存溢出的情况,内存一直居高不下达到了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。过了一个月再问确实是没有再出现内存溢出的问题了,当时想着问题解决了就好,并没有想着记录,后面才发现这种机会难得还是需要记录一下。
标签:SNIMarsManager,System,SNI,SqlClient,内存,Data From: https://www.cnblogs.com/dx5800/p/18510794