我有一个应用程序,我必须从DB获取大量数据。 由于它未能获得所有这些行(它接近2,000,000行...),我在休息时间切断它,每次运行sql查询时每次只运行200,000行。
我使用DataTable输入所有数据(意思是 - 所有2,000,000行都应该在那里)。
前几次运行很好。然后它以OutOfMemoryException失败。
我的代码如下:
private static void RunQueryAndAddToDT(string sql, string lastRowID, SqlConnection conn, DataTable dt, int prevRowCount)
{
if (string.IsNullOrEmpty(sql))
{
sql = generateSqlQuery(lastRowID);
}
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
using (IDbCommand cmd2 = conn.CreateCommand())
{
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = sql;
cmd2.CommandTimeout = 0;
using (IDataReader reader = cmd2.ExecuteReader())
{
while (reader.Read())
{
DataRow row = dt.NewRow();
row["RowID"] = reader["RowID"].ToString();
row["MyCol"] = reader["MyCol"].ToString();
... //In one of these rows it returns the exception.
dt.Rows.Add(row);
}
}
}
if (conn != null)
{
conn.Close();
}
if (dt.Rows.Count > prevRowCount)
{
lastRowID = dt.Rows[dt.Rows.Count - 1]["RowID"].ToString();
sql = string.Empty;
RunQueryAndAddToDT(sql, lastRowID, conn, dt, dt.Rows.Count);
}
}
在我看来好像读者一直在收集行,这就是为什么它只在第三轮或第二轮抛出异常。
不应该使用清理内存吗? 什么可以解决我的问题?
注意:我应该解释一下 - 我别无选择,只能将所有这些行都放到数据表中,因为我稍后会对它们进行一些操作,并且行的顺序很重要,我不能拆分它是因为有时我必须获取某些行的数据并将其设置为一行,依此类推,所以我不能放弃它。
感谢。
答案 0 :(得分:11)
检查您是否正在构建64位进程,而不是32位进程,这是Visual Studio的默认编译模式。为此,右键单击您的项目,属性 - >构建 - >平台目标:x64。与任何32位进程一样,以32位编译的Visual Studio应用程序的虚拟内存限制为2GB。
64位进程没有此限制,因为它们使用64位指针,因此它们的理论最大地址空间为16艾字节(2 ^ 64)。实际上,Windows x64将进程的虚拟内存限制为8TB。然后,内存限制问题的解决方案是以64位编译。
但是,默认情况下,Visual Studio中对象的大小仍限制为2GB。您将能够创建多个组合大小将大于2GB的阵列,但默认情况下不能创建大于2GB的阵列。希望如果您仍然想要创建大于2GB的数组,可以通过向app.config文件添加以下代码来实现:
<configuration>
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
</configuration>
答案 1 :(得分:3)
我认为只是你的内存不足,因为你的DataTable会从你添加的所有行中变得如此之大。
在这种情况下,您可能想尝试不同的模式。
而不是在列表(或DataTable)中缓冲行,您是否可以简单地生成行,因为它们在到达时可以使用?
答案 2 :(得分:3)
由于您使用的是DataTable
,让我分享一下我使用过的随机问题。检查您的构建属性。我有一个问题,DataTable随机抛出一个内存不足的异常。事实证明,项目的构建平台目标设置为Prefer 32-bit
。一旦我取消选择该选项,随机的内存不足就会消失。
答案 3 :(得分:2)
您正在将数据副本存储到dt
。您只是存储太多以至于机器内存不足。所以你几乎没有选择:
要增加可用内存,可以向机器添加物理内存。请注意,32位计算机上的.NET进程将无法访问超过2GB的内存(如果在boot.ini
中启用3GB开关,则为3GB),因此您可能需要切换到64位(机器和进程)如果你想解决比这更多的记忆。
检索较少的数据可能是要走的路。根据您要实现的目标,您可以对数据的子集执行任务(甚至可能在单个行上执行)。如果您正在执行某种聚合(例如,根据数据生成摘要或报告),您可以使用Map-Reduce。