我有一个动态查询,返回大约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;
}
答案 0 :(得分:43)
第一次成功运行, 但是,如果我再次运行它,我会继续 System.OutOfMemoryException。什么 这可能是一些原因 发生?
无论其他人说了什么,该错误与忘记处理您的DBCommand或DBConnection无关,并且您不会通过处置其中任何一个来修复错误。
错误与包含近600,000行数据的数据集有关。显然,您的数据集占用了计算机可用内存的50%以上。显然,当您在第一个数据集被垃圾回收之前返回相同大小的另一个数据集时,您将耗尽内存。就这么简单。
您可以通过以下几种方式解决此问题:
考虑返回较少的记录。我个人无法想象返回600K记录对用户有用的时间。要最小化返回的记录,请尝试:
将查询限制为前1000条记录。如果查询返回的结果超过1000个,请通知用户缩小搜索结果范围。
如果您的用户真的坚持一次看到那么多数据,请尝试分页数据。请记住:谷歌从未向您展示一次搜索的全部22个bajillion结果,它一次显示20条左右的记录。谷歌可能不会同时在内存中保存所有22亿条结果,它可能会发现它更有效地重新查询其数据库以生成新页面。
如果您只需要遍历数据并且不需要随机访问,请尝试返回datareader。 datareader一次只能将一条记录加载到内存中。
如果这些都不是一个选项,那么在使用以下方法之一调用方法之前,需要强制.NET释放数据集使用的内存:
删除对旧数据集的所有引用。任何坚持数据集参考的东西都会阻止它被内存回收。
如果无法清空对数据集的所有引用,请清除数据集中的所有行以及绑定到这些行的所有对象。这将删除对数据行的引用,并允许它们被垃圾收集器吃掉。
我不相信你需要致电GC.Collect()
强制进行循环。调用GC.Collect()
通常不是一个坏主意,因为足够的内存压力会导致.NET自己调用垃圾收集器。
注意:在数据集上调用Dispose不会释放任何内存,也不会调用垃圾收集器,也不会删除对数据集的引用。 Dispose用于清理非托管资源,但DataSet没有任何非托管资源。它只实现了IDispoable,因为它来自MarshalByValueComponent,所以数据集上的Dispose方法几乎没用。
答案 1 :(得分:7)
也许你没有处理上一次运行中的先前连接/结果类,这意味着它们仍然在内存中停留。
答案 2 :(得分:3)
你显然没有处理任何事情。
暂时使用实现IDisposable的对象时,请考虑使用“using”命令。
答案 3 :(得分:1)
尝试尽可能地破坏您的大数据,因为我已经面临过很多次此类问题。其中我有10个Lakh记录,有15列。
答案 4 :(得分:0)
哪里失败了?
我同意您的问题可能是600,000行的数据集可能太大了。我看到你将它添加到Session。如果您使用的是Sql会话状态,则还必须序列化该数据。
即使你正确处理了你的对象,如果你运行它两次,一次在会话中,一次在程序代码中,你将始终在内存中至少有2个这个数据集的副本。这永远不会在Web应用程序中扩展。
数学,600,000行,每行甚至1-128位guid将产生9.6兆字节(600k * 128/8)的数据,更不用说数据集开销了。
修剪你的结果。