我知道这个答案对原始发帖者有帮助为时已晚,但我希望它可以帮助遇到类似问题的其他人。
首先,问题出在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");
.
.