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-如果允许,使用()在
块中键入行。无需指出:)