.NET SqlConnection和DataSet导致内存泄漏

时间:2010-08-04 21:29:33

标签: .net sql-server ado.net

我在运行SQL脚本并将结果转储到文件的服务程序中遇到内存泄漏问题。运行产生许多结果行的查询后,进程的内存使用量每次增加50 MB以上,并且不会降低。

以下是打开连接并检索结果的代码:

using (var conn = new SqlConnection(DataSourceInfo.ConnectionString))
{
    conn.Open();

    var scmd = new SqlCommand(query_string, conn);
    scmd.CommandTimeout = 86400;

    var writer = dest.GetStream(); //the writer is disposed of elsewhere

    using (var da = new SqlDataAdapter(scmd))
    using (var ds = new DataSet())
    {
        da.Fill(ds);
        var table = ds.Tables[0];
        var rows = table.Rows;

        if (TaskInfo.IncludeColNames.Value)
        {
            object[] cols = new object[table.Columns.Count];

            for(int i = 0; i < table.Columns.Count; i++)
                cols[i] = table.Columns[i];

            LineFormatter(writer, TaskInfo.FieldDelimiter, null, false, cols);
            writer.WriteLine();
        }

        foreach(System.Data.DataRow r in rows)
        {
            var fields = r.ItemArray;

            LineFormatter(writer, TaskInfo.FieldDelimiter, TaskInfo.TextQualifier, TaskInfo.TrimFields.Value, fields);
            writer.WriteLine();
        }
    }
}

我使用带有sos.dll的WinDbg在执行完成后按类型列出顶级对象,并且该过程有足够的时间用于GC:

79333470      101       166476 System.Byte[]
65245dcc      177      3897420 System.Data.RBTree`1+Node[[System.Data.DataRow, System.Data]][]
0015e680     5560      3968936      Free
79332b9c      342      3997304 System.Int32[]
6524508c   120349      7702336 System.Data.DataRow
793041d0      984     22171736 System.Object[]
7993bec4       70     63341660 System.Decimal[]
79330a00  2203630     74522604 System.String

第二列是对象数量,第三列是总大小。

不应该有任何未完成的System.Data.DataRow对象。它看起来好像在某种程度上被泄露,但我不确定如何。

我做错了什么?

注意:以前的版本使用SqlDataReader来检索行数据,但是这种方法缺少获取列标题(我知道)的方法,并且在DataSet和SqlDatReader之间共享数据集会在某些查询中无声地失败。我不记得那个版本有内存泄漏问题。

4 个答案:

答案 0 :(得分:2)

选择一个DataRow并使用!gcroot查看谁在行中保留引用。请参阅Tracking down managed memory leaks (how to find a GC leak)

答案 1 :(得分:2)

除非LineFormatter正在做一些事情来保留程序生命周期的引用,否则我认为这里没有问题。

您正在对垃圾收集器的工作方式做出一些重大假设。 AFAIK,它的工作基于记忆压力,而不是时间。如果您感觉非常偏执,可以在代码中运行GC.Collect()并查看是否会降低内存使用率,但我绝不会在生产代码中调用GC.Collect() - 只需将其作为测试

还要确保您不依赖任务管理器来告诉您.NET堆中保留了多少内存。相反,您应该查看performance counters in PerfMon来检查托管世界中发生的情况。

答案 2 :(得分:0)

追踪内存泄漏的最佳方法是使用分析器,例如Nant或.Net Memory Profiler。我认为两者都至少有15天的试用期,这足以了解您的需求并诊断内存泄漏。

我使用过.Net Memory Profiler。它非常适合准确地跟踪所持有的内容,以及从AppDomain或任何静态对象到达泄漏内存的路径。它的工作原理是运行您的应用并抓取元数据;你拍摄快照(使用探查器),运行一个泄漏emory的操作,然后进行第二个snapshop并进行比较。您可以隔离两个快照之间的不同之处,并按大小排序,以便快速关闭问题。非常好的工具!

答案 3 :(得分:0)

您可能需要将SqlCommand放入使用块中,或手动处理它。