为什么我得到一个System.OutOfMemoryException

时间:2014-04-28 18:28:47

标签: 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();
    }
}

我知道这里的垃圾收集没有帮助,并且一旦跳过= 900,000,就会在while循环的第一行抛出内存不足异常。

有什么问题,如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

没有计算内存中应该花多少钱(因为可能存在编码问题,可能会轻易地使数据大小翻倍),我将尝试给出一些指示。

从问题的原因开始 - 我的猜测是threeHundred字典导致了大量的分配。 当您将项目添加到上面的字典时,字典将无法知道应预先分配的项目数。这将导致大量重新分配并将所有数据复制到新创建的词典中。 请在添加任何项目之前将大小(使用ctor)设置为threeHundred字典。

请阅读我发表的文章,深入研究词典内幕 - 我相信它会对这些症状有所了解。 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位。 (在任务管理器或项目属性上查看)

希望这有帮助, 奥菲尔。