使用大型数据集和内存限制

时间:2014-06-18 18:55:52

标签: c# out-of-memory large-data

我正在使用一些代码来比较大型对象集合并存储所有匹配项。

不出所料,我刚遇到System.OutofMemoryException

我该如何解决这个问题?

在比较期间,我应该写入内存,然后将其他内容写入磁盘/ rdbms。即创建一个缓冲区。

2 个答案:

答案 0 :(得分:6)

实际上它确实取决于您的环境,尤其是您的操作系统x86或x64。请在此处查看更多详细信息:Memory in depth

1.您有先进的方案,您需要流媒体。确切的解决方案取决于您从哪里提取数据。如果从SQL数据库中提取数据,您可以使用流式传输SqlDataReader,在这种情况下,它与async紧密耦合,示例代码:

using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
{
     if (await reader.ReadAsync())
     {
         if (!(await reader.IsDBNullAsync(0)))
         {
            using (var dataStream = reader.GetStream(0))
            {
                //process the data
            }
          }
      }
}

此链接将显示更多详细信息:Retrieving large data set。但是,请记住,这种方法会强制您在连接字符串中使用异步以及处理异步代码,这总是会增加复杂性,尤其是当您想要使用specs / tests进行覆盖时。

2.另一种方法是批处理,即将数据缓冲到某个可接受的限制,然后公开批处理以消耗代码,之后继续获取新批处理数据,除非一切都已加载,示例代码:

while(true)
{
 int count = 0;
 bool canRead = reader.Read();
 while(canRead)
 {
  canRead = reader.Read();
  count++;
  if (count >= batchSize)
   break;
 }

 if (!canRead)
  break;
}

您可以通过估算1行数据的大小(基于表格架构,msdn article)粗略计算批量的大小,或者只是使其可配置并使用最合适的值。这种方法的主要优点是您需要对代码进行微小的更改,并且代码本身保持同步。缺点是您必须每次都保持活动连接或打开新连接,而是维护您已经读取的记录以及仍需要提取的记录。

最后,这两个选项都会强制您处理一些更高级的问题,例如,如果只获取一部分数据,那么您应该怎么做?之后连接丢失(需要一些故障转移机制),取消的能力某些超时后长时间运行的检索操作等。

总之,如果您不想处理大数据引入的额外复杂性,请将此任务委派给市场上可用的任何内容,即数据库或第三方框架。如果您认为您的团队有足够的技能,那么请继续并自己实施 - 在磁盘文件中保留比较结果,利用内存缓存或只将数据推送到数据库中

答案 1 :(得分:1)

首先,这取决于您使用的处理器架构。如果您使用的是32位架构,则每个进程只有2GB的内存。在这种情况下,你真的受限于你可以存储在那里。然而,64位处理器允许更多的内存,在这种情况下你应该没问题。

另外需要注意的是,内存不足异常并不意味着根本没有足够的内存,这意味着进程无法分配连续的所需内存块。如果你试图分配一个非常大的对象,例如大小为1GB的数组,那么进程需要找到1GB的连续内存块。

具体的答案取决于您的情况,但总的来说,我会首先尝试切换到64位架构,然后检查分配的最大对象有多大。如果没有什么可以改进的话,你应该开始考虑将一些信息存储到磁盘上。