【问题标题】:C# OutOfMemory Issue when dealing with large data处理大数据时的 C# OutOfMemory 问题
【发布时间】:2012-07-23 15:03:23
【问题描述】:

在我们的应用程序中,我们使用 Windows 服务 生成报告。报告的数据是使用 存储过程SQL Server 获取的。在某些情况下,返回的结果集包含 250,000 条记录(这部分我们无能为力,我们需要一次性使用这些数据,因为我们需要对此进行一些计算)。

问题

我们的应用程序正在读取器中获取此数据,并且我们正在将此数据集转换为自定义对象的自定义集合。由于数据巨大,它无法将完整的数据存储在自定义对象中,从而导致内存不足。当我们在执行记录时看到任务管理器的进程使用率,它会变得非常高,甚至是 CPU 使用率。

我不确定在这种情况下应该怎么做。

  1. 我们能否增加分配给在 CLR 下运行的单个进程的内存大小?
  2. 还有其他解决方法吗?

任何帮助将不胜感激

  1. 为什么我需要一次所有数据:我们需要对完整的结果集进行计算
  2. 我们正在使用 ADO.NET 并将数据集转换为我们的自定义对象(集合)
  3. 我们的系统是 32 位的
  4. 我们无法分页数据
  5. 无法将计算移至 sql server

此堆栈跟踪可能会有所帮助:

引发了“System.OutOfMemoryException”类型的异常。服务器 堆栈跟踪:在 System.Collections.Generic.Dictionary2.ValueCollection.System.Collections.Generic.IEnumerable<TValue>.GetEnumerator() at System.Linq.Enumerable.WhereEnumerableIterator1.MoveNext() 在 System.Collections.Generic.List1.InsertRange(Int32 index, IEnumerable1 集合)在 System.Collections.Generic.List1.AddRange(IEnumerable1 集合) 在 MyProject.Common.Data.DataProperty.GetPropertiesForType(Type t) 中 C:\Ashish-Stuff\Projects\HCPA\Dev Branch\Common\Benefits.Common\Data\DataProperty.shared.cs:第 60 行 MyProject.Common.Data.Extensions.GetProperties[T](T target) in C:\Ashish-Stuff\Projects\HCPA\Dev Branch\Common\Benefits.Common\Data\Extensions.shared.cs:第 30 行 MyProject.Common.Data.Factories.SqlServerDataFactoryContract1.GetData(String procedureName, IDictionary2 参数,Nullable1 languageId, Nullable1 pageNumber,Nullable`1 pageSize)

谢谢, 阿什什

【问题讨论】:

  • 您应该调查是否可以在 SQL 中进行部分或全部计算。例如,您可以将结果汇总到少于 250 万行,然后继续使用 C# 进行处理。
  • 我们需要更多信息。例如操作系统是 32 位还是 64 位操作系统。 2,500,000 条记录不算什么,有些系统可以处理数十亿条记录,它们只是将它们分成片然后组合起来。至于我对这个问题投反对票的原因,您需要提供代码并向我们展示您目前如何处理该问题,以便我们解决您遇到的下划线问题。
  • 可以分页数据吗?缓存呢?
  • 根据计算的性质,您可能不需要一次将其全部加载到内存中(实际上您可以让 SQL Server 进行计算)。我怀疑除了从 32 位移动到 64 位之外,没有什么会增加您的进程的内存。至于解决方法,我们需要更多地了解您的数据结构以及您正在执行的计算。我认为使您要加载的对象尽可能小是不言而喻的(即只保存数据,没有其他内容)。
  • 这是 250 * 1000 用印度数字分组写的 :)

标签: c# sql windows-services garbage-collection out-of-memory


【解决方案1】:

您能否每 1,000 行数据,将您的自定义对象集合序列化到磁盘的某个地方?然后当你返回数据时,从那些文件中分页?

有关您的用例的更多信息以及为什么需要撤回 250 万行数据会有所帮助。

【讨论】:

  • 可能只有 250K。 2,50,000 是您在印度的表达方式。
【解决方案2】:

我的第一个想法是可以通过一些存储过程在 Sql-Server 端进行计算。我怀疑这种方法需要一些 Sql-Server 绝地武士……但无论如何,您考虑过这种方法吗?

【讨论】:

  • 计算有点复杂,还需要其他数据(@c#端已经有)所以不能考虑..
  • 好的,很清楚。要求似乎有点苛刻。然后我建议您不通过 dataadapter 将数据加载到数据集中,而是通过 datareader 将数据加载到自定义实例(结构?)的集合中,使用string.Interns(string s) method 和谷歌进一步了解“c# memory optimization”主题,如this onethis ;
【解决方案3】:

我希望看到一个代码示例突出显示您从何处得到此错误。是在数据本身上拉(填充阅读器)还是创建对象并将其添加到自定义集合(填充集合)。

我以前也遇到过类似的问题,处理非常大的数据集,但在尽可能长时间地将其保留在流中时取得了巨大的成功。流会将数据直接保存在内存中,并且在您完成构建对象之前,您永远不会真正拥有可以直接访问整个混乱的任何东西。现在,鉴于堆栈跟踪显示“MoveNext”操作的错误,这可能不适合您。然后我会说尝试分块数据,一次抓取 10k 行之类的,我知道这可以用 SQL 来完成。但它会使数据读取时间更长。

编辑

如果您将其从数据库读取到本地流中,然后传递(注意不要关闭它),那么您不应该遇到这些问题。创建一个数据包装类,您可以使用开放流和开放阅读器传递该类。将数据存储在流中,然后使用包装函数从中读取您需要的特定数据。诸如GetSumOfXField()AverageOfYValues() 之类的东西等等......数据永远不会在活动对象中,但您不必为它继续返回数据库。

伪示例

    public void ReadingTheDataFunction()
    {
        DBDataReader reader = dbCommand.ExecuteReader();
        MyDataStore.FillDataSource(reader)
    }

    private void FillDataSource(DbDataReader reader)
    {
        StreamWriter writer = new StreamWriter(GlobaldataStream);
        while (reader.Read())
            writer.WriteLine(BuildStringFromDataRow(reader));
        reader.close();
    }

    private CustomObject GetNextRow()
    {
        String line = GlobalDataReader.ReadLine();
        //Parse String to Custom Object
        return ret;
    }

从那里你绕过 MyDataStore,只要流和阅读器没有关闭,你就可以移动你的位置,去寻找单个条目,编译总和和平均值等等。你甚至不需要只要您仅通过接口函数与其交互,就知道您不是在处理活动对象。

【讨论】:

  • 我在填充自定义对象时收到此错误。
  • 我认为你唯一的选择就是把它留在内存中。将数据保存或加载到流中并使用阅读器访问它,或者将其分页到磁盘上的一个文件或一组文件中,然后使用阅读器访问它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多