【问题标题】:system.outofmemoryexception When filling DataAdapter?system.outofmemoryexception 填充DataAdapter的时候?
【发布时间】:2011-02-23 14:55:17
【问题描述】:

我必须从数据库中提取150K 记录。我正在使用da.Fill(ds,"Query") 和它的投掷system.outofmemoryexception

Dim daGrid As New SqlDataAdapter(sqlcmd_q)
daGrid.Fill(dsGrid, "Query")
daGrid.Dispose()

我只需要这个数据表。我不能使用 XML。因为我需要将此分配给 MSChartControl 以显示 ScotterPlot。

有什么建议吗?

【问题讨论】:

  • 以有意义的方式聚合数据...
  • 这些记录有多大(以字节为单位)?

标签: c# asp.net visual-studio-2008


【解决方案1】:

我要检查的第一件事是您要返回多少列,以及它们的数据类型是什么。尽管 150K 记录很多,但它不应该给您 OOM 异常,除非每条记录的长度约为 13K(在 32 位机器上)。这向我表明,您返回的字段比您需要的多,或者某些字段可能是非常大的字符串或二进制数据。尝试减少 select 语句,只返回显示绝对需要的字段。

如果这不起作用,您可能需要从 DataTable 移动到自定义数据类型的列表(具有适当字段的类)。

【讨论】:

  • 我觉得有点奇怪。就我而言,我有一台 64 位机器,内存为 128GB,磁盘为 1.8T。该表有 300 万行,大约 800MB。总是OOM。但是,这不会发生在另一台内存较少(64GB)的机器上。我不知道这是怎么回事。
  • or perhaps that some of the fields are very large strings or binary data 假设您有 100 列都定义为 VARCHAR(MAX) 但没有值超过 10 个字符。如果数据库列都是VARCHAR(10),会有什么不同吗?我会假设不会。只有当单元格中的实际值“非常大”时,对吧?
  • @sab669 正确 - 客户端上 varchar(max) 的内存利用率应该(或多或少)实际数据长度,而不是列的大小。
【解决方案2】:

您没有指定查询。确保它只包含您需要的列。

如果您仍然有问题,您可以尝试切换到 64 位(如果您的硬件支持它并且您有超过 2 GB 的可用内存)。

如果这对您没有帮助,您必须减少内存占用。一个可能的选择是在不将所有基础数​​据存储在内存中的情况下渲染绘图。只需一个一个地加载数据,计算坐标并存储它们,而不存储底层记录。也许你甚至可以让查询来做。

【讨论】:

    【解决方案3】:

    我知道这个答案对原始发帖者有帮助为时已晚,但我希望它可以帮助遇到类似问题的其他人。

    首先,问题出在DataTable而不是DataAdapter

    问题可能是您确实内存不足(在这种情况下,我的回答将无济于事)。您可以通过数学来确定是否可能是这种情况 - 记录数 x 每条记录的字节数猜测。如果在 32 位平台上接近 2GB 或在 64 位平台上您的可用 RAM 接近,那么您唯一的选择是减少记录数、字段数或提出使用 DataReader 而不是 DataTable 的方法。

    在您的情况下,您有 150k 条记录,假设每条记录需要 1KB 的内存,这给我们一个 150MB 的整数。即使在具有 2GB RAM 的 32 位机器上也应该没问题(前提是没有太多类似的内存分配正在进行)。在您的情况下,您有一台具有 128GB RAM(不错)的 64 位机器。所以从逻辑上讲,你不应该出现内存错误。

    那么是什么导致了这个问题?它是大对象堆 (LOH)。为什么? DataTable 创建一个数组来保存这些记录。我的理解是它创建了一个 50 个数组,然后随着记录的添加而增长。任何超过 85,000 字节的内存分配都将来自大对象堆。 (你在一个 64 位平台上,所以一旦你达到 10,625 条记录,那么分配将开始来自大对象堆。)大对象堆的问题在于它没有被压缩。所以可能有很多可用空间,但没有一个足够大的连续块。在 .net 4.5 中,微软在合并相邻片段方面对其进行了改进,但不会重新组织这些片段以创建更大的可用空间块。最终的结果是,根据我的经验,一旦您误入 LOH,就会出现内存不足异常只是时间问题。

    解决方案?

    对我有用的是设置DataTable 的初始容量。从数据库中提取记录时,这意味着首先进行计数,因此这确实是以额外的数据库查询为代价的,然后:

    .
    .
    dsGrid.InitialCapacity = count;
    daGrid.Fill(dsGrid, "Query");
    .
    .
    

    虽然这不能避免误入 LOH,但这应该意味着它只进行一次分配而不是多次分配。因此,除了避免内存不足异常之外,您还应该获得性能提升(因需要额外的数据库查询而抵消)。

    您可以让 .net 垃圾收集器压缩大对象堆,但您只能告诉它在下次运行时执行此操作。如果我知道我正误入大对象堆,我倾向于使用它。这可能有点矫枉过正,但请考虑将我的上述建议修改为:

    .
    .
    dsGrid.InitialCapacity = count;
    if (count > 10625)
    {
        System.Runtime.GCSettings.LargeObjectHeapCompactionMode =
            System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
    }
    daGrid.Fill(dsGrid, "Query");
    .
    .
    

    【讨论】:

    • 谢谢 Brian,InitialCapacity 是否等于 DataTable.MinimumCapacity ?
    • 简短回答:是的。
    猜你喜欢
    • 2013-04-11
    • 2011-08-13
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    • 2010-12-01
    • 2012-06-29
    • 2011-06-22
    • 1970-01-01
    相关资源
    最近更新 更多