c# - 为什么我会收到 System.OutOfMemoryException

标签 c# .net linq linq-to-sql out-of-memory

我正在使用 Linq to Sql。 这是代码:

Dictionary<string, int> allResults;
using (var dc= new MyDataContext())
{
    dc.CommandTimeout = 0;
    allResults = dc.MyTable.ToDictionary(x => x.Text, x => x.Id);
}

它在 64 位机器上运行,编译是 AnyCPU。它抛出 System.OutOfMemoryException。

这会访问 SQL Server 数据库。 Id 字段映射到 SQL Server int 字段,Text 字段映射到 Text(nvarchar(max)) 字段。运行 select COUNT(*) from TableName 得到 1,173,623 条记录,运行 select sum(len(Text)) from TableName 得到 48,915,031 条记录。由于 int 是一个 32 位整数,id 应该只占用 4.69MB 的空间,字符串应该不到 1GB。所以我们甚至没有违反 2GB/对象的限制。

然后我以这种方式更改代码:

Dictionary<string, int> allResults;
using (var dc = new MyDataContext())
{
   Dictionary<string, int> threeHundred;
   dc.CommandTimeout = 0;
   var tot = dc.MyTable.Count();
   allResults = new Dictionary<string, int>(tot);
   int skip = 0;
   int takeThis = 300000;
   while (skip < tot)
   {
      threeHundred = dc.MyTable.Skip(skip).Take(takeThis).ToDictionary(x => x.Text, x => x.Id);
      skip = skip + takeThis;
      allResults = allResults.Concat(threeHundred).ToDictionary(x => x.Key, x => x.Value);
      threeHundred = null;
      GC.Collect();
    }
}

我了解到这里的垃圾收集没有帮助,一旦 skip = 900,000,就会在 while 循环的第一行抛出内存不足异常。

出了什么问题,我该如何解决?

最佳答案

在不考虑应该占用多少内存的情况下(因为可能存在编码问题,很容易使数据大小加倍),我将尝试提供一些建议。

从问题的原因开始 - 我的猜测是 threeHundred 字典导致了大量分配。 当您像上面那样将项目添加到字典时,字典将无法知道它应该预先分配多少项目。这将导致大量重新分配和处理所有数据到新创建的字典。 在向 threeHundred 字典添加任何项目之前,请设置一个大小(使用 ctor)。

请阅读我发表的这篇深入探讨词典内部结构的文章 - 我相信它会阐明这些症状。 http://www.codeproject.com/Articles/500644/Understanding-Generic-Dictionary-in-depth

此外,当尝试填充如此大量的数据时,我建议完全控制过程。 我的建议:

  • 在 Dictionary 中预分配插槽(直接在 DB 上使用 Count 查询,并将其传递给 Dictionary ctor)
  • 使用 DataReader 填充这些项目,而无需将所有查询结果加载到内存中。 如果你知道一个事实(提前知道这一点非常重要) - 考虑使用 string.Intern - 仅当有很多重复项时! - 你应该测试看看它是如何工作的
  • 内存分析代码——你应该只看到字典的一个分配,以及查询中项目数量的字符串(int 是值类型——因此它没有作为对象分配在堆上,而是它位于字典中。

无论哪种方式,您都应该检查您是在 32 位还是 64 位上运行。 .Net 4.5 更喜欢 32 位。 (在任务管理器或项目属性中查看)

希望对您有所帮助, Ofir.

关于c# - 为什么我会收到 System.OutOfMemoryException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23348569/

相关文章:

c# - LINQ to SQL 数据库行未更新

c# - Dictionary<string,string> 到 Dictionary<Control,object> 使用 IEnumerable<T>.Select()

c# - 如何阻止 GraphicsPath 关闭

c# - .Net core IHostedService后台任务异常不会终止应用

C# 字符/字节编码相等

c# - 字段和属性如何关联?

.net - Web Deploy 能否为目标服务器的GAC 打包本地程序集?

c# - 如何将 dll 配置文件添加到添加 dll 作为引用的项目中?

c# - ASP.NET Core 2.1 应用程序未获得正确的 appsettings.json

c# - 数组上的 LINQ 过滤器