You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行SQL脚本后内存居高不下,list=null/clear()无效的解决办法

内存持续增长问题解决方案

问题根源分析

  1. 字符串拼接引发大量内存碎片:GetSqlList中使用string类型的commandText进行拼接,C#里string是不可变类型,每次拼接都会生成新的字符串对象。400KB的SQL文件会产生大量临时字符串,这些对象不仅占用大量内存,还会加重GC回收压力。
  2. StreamReader资源未可靠释放:虽然调用了reader.Close(),但未使用using语句,无法保证异常场景下资源也能及时释放,可能导致句柄和内存泄漏。
  3. List扩容残留内存:默认创建的List<string>初始容量较小,添加元素时会频繁触发扩容,旧的数组对象可能暂时无法被GC回收。

具体修复步骤

1. 替换字符串拼接为StringBuilder

用StringBuilder替代string进行SQL语句拼接,避免频繁创建新字符串对象:

private static List<string> GetSqlList(string sqlPath, string database)
{
    var sqllist = new List<string>();
    try
    {
        // 预设足够初始容量,减少扩容次数
        var commandText = new StringBuilder(1024 * 1024);
        string varLine = "";
        // 使用using自动释放StreamReader资源
        using (var reader = new StreamReader(sqlPath, Encoding.GetEncoding("gb2312")))
        {
            while (reader.Peek() > -1)
            {
                varLine = reader.ReadLine();
                varLine = varLine.Replace("[kms]", "[" + database + "]");
                if (string.IsNullOrEmpty(varLine))
                {
                    continue;
                }
                var trimmedLine = varLine.Trim();
                if (trimmedLine != "GO" && trimmedLine != "go")
                {
                    commandText.Append(varLine);
                    commandText.Append("\r\n");
                }
                else
                {
                    sqllist.Add(commandText.ToString());
                    commandText.Clear(); // 清空复用StringBuilder,避免重复创建
                }
            }
        }
        if (commandText.Length > 0)
        {
            sqllist.Add(commandText.ToString());
        }
        return sqllist;
    }
    catch (Exception ex)
    {
        // 建议添加日志记录,便于后续排查问题
        return null;
    }
}

2. 优化UpgradeDatabase函数

执行完SQL后主动清理引用,必要时触发GC回收(优先解决根本问题,GC触发为可选操作):

public static void UpgradeDatabase(DbHelper db, string deployPath)
{
    try
    {
        string sqlPath = Path.Combine(deployPath, "Source", "UpdateDataBase.sql");
        if (!System.IO.File.Exists(sqlPath))
        {
            throw new FileNotFoundException("未找到UpdateDataBase.sql文件", sqlPath);
        }
        var sqlList = GetSqlList(sqlPath, db.Database);
        if (sqlList == null)
        {
            throw new Exception("解析SQL文件失败");
        }
        db.ExecuteSqlList(sqlList);
        // 主动清空列表并解除引用
        sqlList.Clear();
        sqlList = null;
        
        // 可选:内存压力大时触发GC(不建议频繁调用)
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
    catch (Exception ex)
    {
        // 建议添加异常日志,避免吞掉异常导致问题无法定位
    }
}

3. 检查DbHelper.ExecuteSqlList实现

确认ExecuteSqlList中没有持有sqlList的引用,且数据库连接、命令等资源都通过using语句正确释放(比如SqlConnection、SqlCommand必须包裹在using中),这是常见的内存泄漏隐藏点。

额外建议

  • 若该函数需要频繁执行,可缓存解析后的SQL列表,避免重复读取和解析文件。
  • 使用Visual Studio的内存诊断工具抓取内存快照,查看未被回收的对象类型,精准定位泄漏点。

内容的提问来源于stack exchange,提问作者remilialover

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:18:29