我有一个动态查询,返回大约 590,000 条记录。第一次运行成功,但如果再次运行,我会不断收到 System.OutOfMemoryException
。发生这种情况的原因有哪些?
错误发生在这里:
public static DataSet GetDataSet(string databaseName,string
storedProcedureName,params object[] parameters)
{
//Creates blank dataset
DataSet ds = null;
try
{
//Creates database
Database db = DatabaseFactory.CreateDatabase(databaseName);
//Creates command to execute
DbCommand dbCommand = db.GetStoredProcCommand(storedProcedureName);
dbCommand.CommandTimeout = COMMAND_TIMEOUT;
//Returns the list of SQL parameters associated with that stored proecdure
db.DiscoverParameters(dbCommand);
int i = 1;
//Loop through the list of parameters and set the values
foreach (object parameter in parameters)
{
dbCommand.Parameters[i++].Value = parameter;
}
//Retrieve dataset and set to ds
ds = db.ExecuteDataSet(dbCommand);
}
//Check for exceptions
catch (SqlException sqle)
{
throw sqle;
}
catch (Exception e)
{
throw e; // Error is thrown here.
}
//Returns dataset
return ds;
}
以下是单击按钮时运行的代码:
protected void btnSearchSBIDatabase_Click(object sender, EventArgs e)
{
LicenseSearch ls = new LicenseSearch();
DataTable dtSearchResults = new DataTable();
dtSearchResults = ls.Search();
Session["dtSearchResults"] = dtSearchResults;
Response.Redirect("~/FCCSearch/SearchResults.aspx");
}
else
lblResults.Visible = true;
}
最佳答案
It runs successfully the first time, but if I run it again, I keep getting a System.OutOfMemoryException. What are some reasons this could be happening?
无论其他人怎么说,该错误与忘记处置 DBCommand 或 DBConnection 无关,并且您不会通过处置其中任何一个来修复错误。
该错误与包含近 600,000 行数据的数据集有关。显然,您的数据集消耗了机器上超过 50% 的可用内存。显然,当您在第一个数据集被垃圾收集之前返回相同大小的另一个数据集时,您将耗尽内存。就这么简单。
您可以通过以下几种方式解决此问题:
考虑返回更少的记录。我个人无法想象返回 600K 条记录对用户有用的时候。要最大限度地减少返回的记录,请尝试:
将查询限制为前 1000 条记录。如果查询返回的结果超过 1000 个,则通知用户缩小搜索结果范围。
如果您的用户确实坚持要一次查看那么多数据,请尝试对数据进行分页。请记住:Google 永远不会一次向您显示全部 22 亿条搜索结果,它一次向您显示 20 条左右的记录。 Google 可能不会同时将所有 22 亿个结果保存在内存中,它可能发现重新查询数据库以生成新页面的内存效率更高。
如果您只需要迭代数据并且不需要随机访问,请尝试返回 datareader。数据读取器一次仅将一条记录加载到内存中。
如果这些都不是一个选项,那么您需要在使用以下方法之一调用您的方法之前强制 .NET 释放数据集使用的内存:
删除对旧数据集的所有引用。任何保留数据集引用的内容都会阻止它被内存回收。
如果您无法清空对数据集的所有引用,请清除数据集中的所有行以及绑定(bind)到这些行的任何对象。这会删除对数据行的引用,并允许垃圾收集器将其吞噬。
我认为您不需要调用 GC.Collect()
来强制生成循环。调用 GC.Collect() 通常不是一个好主意,因为足够的内存压力会导致 .NET 自行调用垃圾收集器。
注意:对数据集调用 Dispose 不会释放任何内存,也不会调用垃圾收集器,也不会删除对数据集的引用。 Dispose用于清理非托管资源,但DataSet没有任何非托管资源。它只实现了 IDispoable,因为它是 MarshalByValueComponent 固有的,因此数据集上的 Dispose 方法几乎没有用。
关于asp.net - 引发了 'System.OutOfMemoryException' 类型的异常。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/356645/