C# Command.Prepare()是否导致内存泄漏?
我继承了这个科学建模项目的一些代码,我的同事和我都被这个问题难住了。写这篇文章的人现在已经不在了,所以我们不能问他(算了吧) 在数据访问层内部,有一个C# Command.Prepare()是否导致内存泄漏?,c#,database,postgresql,ado.net,memory-leaks,C#,Database,Postgresql,Ado.net,Memory Leaks,我继承了这个科学建模项目的一些代码,我的同事和我都被这个问题难住了。写这篇文章的人现在已经不在了,所以我们不能问他(算了吧) 在数据访问层内部,有一个insert()方法。这听起来像是在向数据库中插入记录。在模拟过程中,被建模的各种对象使用它来告诉数据库它们自己的情况 然而,我们注意到,在经过相当多的数据库插入之后的较长的模拟过程中,我们最终得到了连接超时。因此,我们提高了超时限制,然后开始从PostgreSQL中获取“内存不足”错误。我们最终将问题定位到IDbCommand对象使用Prepar
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
...
}
}
EDIT:(响应第一个给定的答案)所有数据库对象都在
finally
块中处理。为了节省空间,我把那部分剪掉了。我们对此做了一些研究,但没有任何区别,所以我认为这不是问题所在。您会注意到IDbCommand和IDataReader都实现了。无论何时创建IDisposable对象的实例,您都应该将其包装在一个文件中,或者在完成后调用它。如果不这样做,最终会泄漏资源(有时是内存以外的资源)
在你的代码中试试这个
using (IDbCommand cmd = createQuery(".toInsert", obj))
{
cmd.Prepare(); // This is what is screwing things up
...
//the rest of your example code
...
}
编辑以专门谈论准备
我可以从代码中看出,您正在准备命令,但从未重用它
准备命令背后的想法是,准备命令需要额外的开销,但是每次使用命令时,它将比未准备语句更有效。如果您有一个要大量重用的命令,那么这是一个很好的选择,它是一个权衡开销是否值得命令的性能提高的方法
因此,在您向我们展示的代码中,您正在准备命令(支付所有开销),但没有得到任何好处,因为您随后会立即丢弃该命令
我要么循环使用prepared命令,要么放弃对prepare语句的调用
我不知道为什么准备好的命令会泄漏,但首先不应该准备这么多命令(尤其是一次性命令)。Prepare()方法旨在使查询运行更高效。实现这一点完全取决于提供商。典型的方法是创建一个临时存储过程,让服务器有机会预解析和优化查询
像这样的代码有两种泄漏内存的方法。一个是典型的.NET细节,IDbCommand类的实际实现总是有一个Dispose()方法,在终结器线程执行之前显式释放资源。我看你的代码片段中没有用到它。但在这种情况下,很难在不运行垃圾收集器的情况下消耗所有内存。您可以从Perfmon.exe判断并观察垃圾收集器的性能计数器
下一个候选者更加阴险,因为您正在使用大量本机代码。数据库提供程序并不是那么简单。自由和开放源码软件的设计倾向于让你从中摆脱错误。源代码可用是有原因的。Perfmon.exe再次诊断,看到托管堆没有超出界限,但私有字节爆炸是一个致命的漏洞
如果您不想调试提供程序,可以对该语句进行注释 是的,我将把
IDbCommand cmd=null代码>-如果允许,使用()在块中键入行。无需指出:)