5

我有一个以 MS SQL Server 2005 作为后端的 Windows 窗体应用程序。我已经在表单中编写了代码来使用 SqlConnection、SqlCommand 对象调用一些存储过程,并且我正确地处理了所有内容。

我已经通过调用处理了 sqlcommand 对象

oSqlCommand.Dispose()

但我目睹了我的应用程序消耗了大量内存。我基本上将大型 XML 文件作为 SqlParameters 传递。

我最终决定使用 RedGate 内存分析器对其进行内存分析,我注意到System.Data.SqlClient.SqlParameters没有处理。

对此有何见解?

谢谢

NLV

4

6 回答 6

15

我看到这个:

我妥善处置一切。

还有这个:

我已经通过调用处理了 sqlcommand 对象oSqlCommand.Dispose()

然而,这些是相互排斥的!如果你.Dispose()直接打电话,你做错了。具体来说,您保留打开异常使程序跳过对该Dispose()方法的调用的可能性。处理命令的“正确”方法使用 using 块创建它,如下所示:

using (SqlCommand cmd = new SqlCommand("sql string here"))
{
    // use the command here
} // compiler transforms your code to make sure .Dispose() is called here

现在,我从这个问题中得出结论,这不是目前的主要问题,但值得开车回家。

至于关于参数的问题:SqlParameters 不实现 IDisposable。因此,您不要直接处置它们。它们是完全托管的资源,这意味着它们在不再可访问后的某个时间点被垃圾收集器清除。你不必做任何事情来自己清理它们。

如果您可以认真地证明 SqlParameter 对象在它们应该存在很长时间之后仍然存在,这意味着您在某处持有对它们的引用。例如,也许您正在某处“缓存”旧的 SqlCommand 对象,而这些对象又会保留它们的所有参数。不要那样做。查找并消除仍然引用 SqlParameters 的任何内容,垃圾收集器将为您清理它们。

更新:

重新阅读您的问题后,听起来 xml 参数最终出现在大对象堆上。.Net 中的垃圾收集器是分代的——它不会在每次运行时都清理所有内容。当一个对象移动到更高的一代时,它更有可能停留一段时间。大对象堆基本上是最后一代,根本没有清理多少。更重要的是,它永远不会被压缩,以至于随着时间的推移它会分裂。这可能会导致程序保留比它需要的更多的数据。您需要做的是尝试找到一种方法来避免将参数的整个 xml 数据加载到内存中,这样它就永远不会进入大对象堆。改用文件流或类似的东西。

于 2010-06-09T19:19:23.610 回答
6

因为SqlParameteris not IDisposable,所以处理它不是问题;并且通常在整理引用等方面几乎没有什么好处,因为它仍然受同一个 GC 的约束。

如果听起来您不小心保留了对SqlCommand. 但是如果你确定你已经完成了,你可以尝试将每个显式设置.Valuenull,并调用Clear()参数列表。但这实际上只是掩盖了您坚持死命令的事实。

于 2010-06-09T19:22:40.637 回答
3

Dispose 不释放它的参数,只释放它的内部 SqlMetaData 缓存...顺便说一句,参数不会自动释放是正常的,因为您可以在释放命令后传入不应该释放的东西... + SqlParameter 没有实现处置其中之一,因为它不包含非托管资源....

于 2010-06-09T19:16:42.303 回答
0

如果不对此进行测试,我可以想到两件事可能对您有所帮助。有了 ,SqlParameters您可以使用finalize()释放资源的方法。另外,您是否通过using块运行所有 Sql 命令?如果是这样,当 using 块完成时,您的资源应该被回收,它将消除您的内存泄漏问题。

于 2010-06-09T19:20:08.953 回答
0

我用过这个模式是几个项目没有任何问题

public partial class StoredProcedures
{
    [SqlProcedure()]
    public static void InsertCurrency_CS(
        SqlString currencyCode, SqlString name)
    {
        using (SqlConnection conn = new SqlConnection("context connection=true"))
        {
            SqlCommand InsertCurrencyCommand = new SqlCommand();
            SqlParameter currencyCodeParam = new SqlParameter("@CurrencyCode", SqlDbType.NVarChar);
            SqlParameter nameParam = new SqlParameter("@Name", SqlDbType.NVarChar);



            InsertCurrencyCommand.CommandText =
                "INSERT Sales.Currency (CurrencyCode, Name, ModifiedDate)" +
                " VALUES(@CurrencyCode, @Name)";

            InsertCurrencyCommand.Connection = conn;

            conn.Open();
            InsertCurrencyCommand.ExecuteNonQuery();
            conn.Close();
        }
    }
}

参考:http: //msdn.microsoft.com/en-us/library/5czye81z%28VS.80%29.aspx

于 2010-06-09T19:32:56.327 回答
0

如果您确定保留最后一个引用的是 SqlParameter,您可以做几件事。

首先,尝试将您的 XML 作为字符串传递(并在存储过程中使用 OPENXML 来处理它),看看是否有一个简单的对象和更多的控制会有所帮助。

其次,制作自己的 SqlParameter-s,将它们保存在 Dictionary 中,然后执行以下操作:

foreach (SqlParameter param in parameters.Values)
    command.Parameters.Add(param);

然后在完成命令运行并处理命令并关闭(如果仍然打开)并处理连接后,进入字典,将 null 显式分配为 SqlParameter.Value (或者,将字符串 ref 从 .Value 获取到本地 var,将 String.Empty 分配给 .Value,然后将 null 分配给本地 var - 仅当 SqlParameter.Value 抱怨直接为 null 时。然后将 null 分配给字典项(即 SqlParameter 的引用),然后将 null 分配给字典。

在更简单的情况下,您可以只保留一个关键 SqlParameter 的引用并跳过字典。关键是要保持显式分配空值- 字符串的最后一个引用,然后是包含它的 SqlParameter 的最后一个引用。

请记住,这涉及到几件事。它从根本不解析中间层的 XML 开始 - 只是将其发送到 SQL 并以显式取消引用结束。如果您的代码实际上是在动态构建 XML,请将其设为一个大的直字符串来尝试。

如果仅此一项并不能降低内存压力,那么您将不得不强制执行显式 GC 收集,但为此您必须做一些阅读并计划合理的时间间隔,因为 GC 成本很高,即如果您在每次请求后启动 GC-in就像一只疯狂的兔子一样,您将在 CPU 周期上付出很多。

Also since you didn't say how big you data actually is and on what kind of hardware are you running and if your mid tier running under IIS it's hard to speculate about possible further options, like to have IIS run multiple worker processes and just recycle them when they get too bloated. For really huge memory consumption and genuine pass-through mid-tier (meaning no cache buildups) that can be faster than fiddling with gc but we are talking really huge data in order to enter that area.

于 2010-09-18T16:35:18.533 回答