我在这个科学建模项目中继承了一些代码,我和我的同事都被这个问题困扰了。写这篇文章的人现在已经不见了,所以我们不能问他(去图)。
在数据访问层内,有insert()
方法。这听起来像 - 它将记录插入数据库。它被建模的各种对象使用,以在模拟过程中告诉数据库他们自己。
但是,我们注意到在经过大量数据库插入后的较长时间模拟中,我们最终得到了连接超时。所以我们提高了超时限制,然后我们开始从PostgreSQL获得“内存不足”错误。我们最终将问题确定为IDbCommand
对象使用Prepare()
的行。离开它会导致内存使用无限期地上升。注释掉这一行会导致代码工作正常,并消除所有内存问题。 Prepare()
导致这种情况的原因是什么?我在文档中找不到任何解释这一点的内容。
代码的压缩版本如下。
public virtual void insert(DomainObjects.EntityObject obj)
{
lock (DataBaseProvider.DataBase.Connection)
{
IDbCommand cmd = null;
IDataReader noInsertIdReader = null;
IDataReader reader= null;
try
{
if (DataBaseProvider.DataBase.Validate)
{ ... }
// create and prepare the insert command
cmd = createQuery(".toInsert", obj);
cmd.Prepare(); // This is what is screwing things up
// get the query to retreive the sequence number
SqlStatement lastInsertIdSql = DAOLayer...getStatement(this.GetType().ToString() + ".toGetLastInsertId");
// if the obj insert does not use a sequence, execute the insert command and return
if (lastInsertIdSql == null)
{
noInsertIdReader = cmd.ExecuteReader();
noInsertIdReader.Close();
return;
}
// append the sequence query to the end of the insert statement
cmd.CommandText += ";" + lastInsertIdSql.Statement;
reader = cmd.ExecuteReader();
// read the sequence number and set the objects id
...
}
// deal with some specific exceptions
...
}
}
编辑:(响应第一个给出的答案)所有数据库对象都放在finally
块中。我只是为了节省空间而把这部分切掉了。我们已经玩了一点,并没有任何区别,所以我认为这不是问题。
答案 0 :(得分:3)
您会注意到IDbCommand和IDataReader都实现了IDisposable。每当您创建IDisposable对象的实例时,您应该将其包装在using statement中,或者在完成后调用Dispose。如果不这样做,你最终会泄漏资源(有时候资源不仅仅是内存)。
在您的代码中尝试此操作
using (IDbCommand cmd = createQuery(".toInsert", obj))
{
cmd.Prepare(); // This is what is screwing things up
...
//the rest of your example code
...
}
编辑专门讨论准备
我可以从代码中看到您正在准备命令,然后再也不会重复使用它。
准备命令背后的想法是,它需要额外的开销来准备,但是每次使用命令时它都会比非预备语句更有效。如果你有一个你要重复使用的命令,这是很好的,并且是否是开销是否值得命令的性能提升。
所以在你向我们展示的代码中,你正在准备命令(支付所有开销)并且没有任何好处,因为你会立即抛弃命令!
我会回收准备好的命令,或者只是将调用放弃到prepare语句。
我不知道为什么准备好的命令会泄漏,但你不应该首先准备这么多的命令(特别是单用命令)。
答案 1 :(得分:2)
Prepare()方法旨在使查询更有效地运行。实现这一点完全取决于提供者。典型的创建临时存储过程,使服务器有机会预先解析和优化查询。
有几种方法可能会泄漏内存。一个是典型的.NET细节,IDbCommand类的实际实现总是有一个Dispose()方法,在终结器线程执行之前显式释放资源。我没有看到它在您的代码段中使用过。但在这种情况下,不太可能在没有运行垃圾收集器的情况下消耗所有内存。您可以从Perfmon.exe中了解并观察垃圾收集器的性能计数器。
下一个候选人更阴险,你正在使用 big 大块本机代码。 Dbase提供商并不那么简单。 FOSS类型往往旨在让您从中获取错误。源代码可用有原因。 Perfmon.exe再次诊断出来,看到托管堆没有增长到超出界限但私有字节爆炸是一个死的赠品。
如果您不想调试提供程序,则只需对语句进行评论即可。