system.outofmemoryexception填充DataAdapter时?

时间:2011-02-23 14:55:17

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

我必须从DB中提取150K条记录。我正在使用da.Fill(ds,"Query")并投掷system.outofmemoryexception

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

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

有什么建议吗?

3 个答案:

答案 0 :(得分:7)

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

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

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:1)

我知道这个答案对帮助最初的张贴者来说还很晚,但是我希望它可以帮助遇到类似问题的其他人。

首先,它是DataTable,这是问题不是DataAdapter

问题可能是您确实内存不足(在这种情况下,我的回答无济于事)。你可以做数学摸出是否可能如此 - 的记录数×每记录的字节瞎猜。如果在32位平台上该容量接近2GB或在64位平台上的可用RAM,那么您唯一的选择是减少记录数量,字段数量,或者想出一种使用DataReader而不是DataTable的方法。 / p>

在您的情况下,您有150k条记录,假设每个记录需要1KB的内存,这使我们的数字为150MB。即使是32位机的RAM应该罚款2GB的(只要不是很多类似的内存分配的事)。在您的情况下,您有一台具有128GB RAM(不错)的64位计算机。因此,从逻辑上讲,您不应该摆脱内存错误。

那是什么原因引起的呢?这是大对象堆(LOH)。为什么? DataTable创建一个数组来保存这些记录。我的理解是,它创建了一个由50个组成的数组,然后随着记录的增加而增加。超过85,000字节的任何内存分配都将来自大型对象堆。 (您使用的是64位平台,因此得出的结论是,一旦您击中10,625条记录,分配就会从大对象堆开始。)大对象堆的问题在于它没有压缩。因此,有可能是很多的自由空间,但没有一个连续的块是足够大的。借助.net 4.5,Microsoft在合并相邻片段方面进行了改进,但不会重组这些片段以创建更大的可用空间块。最终结果是,一旦您误入LOH,这只是时间问题,然后您就会收到“内存不足”异常。

解决方案?

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

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

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

您可以使.net垃圾回收器压缩大对象堆,但是只能告诉它下次运行时再做。我倾向于使用这个,如果我知道我误入大对象堆。这可能是矫kill过正,但请考虑将我的上述建议修改为:

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