C# 从大型对象(特别是数据集)回收内存

C# 从大型对象(特别是数据集)回收内存,c#,windows-services,garbage-collection,dataset,C#,Windows Services,Garbage Collection,Dataset,我继承了一个Windows服务,该服务通过远程处理接受请求,以分析数据库中潜在的大量数据。它通过将原始数据加载到数据集中,将数据解析为树状对象结构,然后运行分析来检索原始数据 我遇到的问题是,如果分析了一组非常大的数据,即使我主动强制垃圾收集,也不会在分析完成后将所有内存返回到系统。例如,如果分析了一组500MB的数据,windows服务将从启动时的基线~10MB增加到~500MB,然后在GC.Collect之后下降到~200MB,并且不会再下降,即使是一夜之间。恢复内存的唯一方法是停止并重新启

我继承了一个Windows服务,该服务通过远程处理接受请求,以分析数据库中潜在的大量数据。它通过将原始数据加载到数据集中,将数据解析为树状对象结构,然后运行分析来检索原始数据

我遇到的问题是,如果分析了一组非常大的数据,即使我主动强制垃圾收集,也不会在分析完成后将所有内存返回到系统。例如,如果分析了一组500MB的数据,windows服务将从启动时的基线~10MB增加到~500MB,然后在GC.Collect之后下降到~200MB,并且不会再下降,即使是一夜之间。恢复内存的唯一方法是停止并重新启动服务。但是如果我运行一个小的分析,服务从~10MB到~50MB,然后下降到~20MB左右。也不是很大,但在分析完成后,大小数据之间的最终利用率存在巨大差异

这本身不是内存泄漏,因为如果我反复运行大型分析,每次完成时,总内存都会下降到~200MB

这是一个问题,因为windows服务在共享服务器上运行,我不能让进程一直占用大量内存。分析完成后,如果它出现峰值,然后又下降,这没关系,但这会出现峰值,并且部分下降到一个不可接受的高数字。典型的情况是运行分析,然后闲置几个小时

不幸的是,这个代码库非常大,其中很大一部分被编码为使用专有数据访问层返回的数据表,因此使用另一种方法加载数据不是我希望的选项,将所有数据加载到内存中只是为了循环它是没有意义的

因此,我的问题是:

1为什么运行大数据集会使内存利用率回落到~200MB,而运行小数据集会使内存利用率回落到~20MB?很明显,它以某种方式依附在数据集的各个部分上,我就是看不出在哪里

2如果我在数据表的行上循环,或者看不到下面的内容,为什么会有不同

3在分析完成后,如何在不彻底改变体系结构的情况下将内存恢复到合理的水平

我创建了一个小的windows服务/客户端应用程序来重现这个问题。我使用的测试数据库有一个包含一百万条记录的表、一个int PK和两个字符串字段。以下是我尝试过的场景-客户端控制台应用程序通过远程处理在一个循环中调用LoadData十次

1 doWork=true,garbageCollect=true,recordCount=100000。内存增加到78MB,然后稳定在22MB

2道工作=假,垃圾收集=真,记录计数=100000。内存增加到78MB,稳定在19MB。说真的,在不做任何事情的情况下在行上多循环3MB

3道工=假,垃圾收集=假,记录计数=100000。内存增加到大约178MB,然后稳定在78MB。强制垃圾收集显然是在做一些事情,但还不足以满足我的需要

4 doWork=false,garbageCollect=true,recordCount=1000000。内存最高可达500MB,稳定在35MB。当数据集较大时,为什么会稳定在较高的数值

5道工=假,垃圾收集=真,记录计数=1000。它跑得太快,看不到峰值,但稳定在微不足道的12MB

public string LoadData(bool doWork, bool garbageCollect, int recordCount)
{
    var dataSet = new DataSet();

    using (var sqlConnection = new SqlConnection("...blah..."))
    {
        sqlConnection.Open();

        using (var dbCommand = sqlConnection.CreateCommand())
        {
            dbCommand.CommandText = string.Format("select top {0} * from dbo.FakeData", recordCount.ToString());
            dbCommand.CommandType = CommandType.Text;

            using (var dbReader = new SqlDataAdapter(dbCommand))
            {
                dbReader.Fill(dataSet);
            }                    
        }

        sqlConnection.Close();
    }

    // loop over the records

    var count = dataSet.Tables[0].Rows.Count;

    if (doWork)
    {
        foreach (DataRow row in dataSet.Tables[0].Rows) {}
    }

    dataSet.Clear();
    dataSet = null;

    if (garbageCollect)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }

    return string.Format("Record count is {0}", count);
}

你读过这个帖子吗?您使用什么值来确定您的应用程序正在使用xxxMB的内存?你在使用内存分析工具吗?另外,这个帖子中公认的答案有一些很好的见解:好吧,听起来我什么都不担心。