SqlCommand.Dispose() 未处理其中的 SqlParameters - 内存泄漏 - C#.NET

发布于 2024-09-05 04:25:28 字数 390 浏览 2 评论 0原文

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

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

oSqlCommand.Dispose()

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

我最终决定使用 RedGate 内存分析器对其进行内存分析,并且我注意到 System.Data.SqlClient.SqlParameters 未释放。

对此有什么见解吗?

感谢

NLV

I've a windows forms application with MS SQL Server 2005 as the back end. I have written code in the form to call few stored procedures using SqlConnection, SqlCommand objects and i properly dispose everything.

I've disposed sqlcommand object by calling

oSqlCommand.Dispose()

But i witnessed my application consuming huge amount of memory. I basically pass large XML files as SqlParameters.

I finally decided to memory profile it using RedGate Memory profiler and i noticed that the System.Data.SqlClient.SqlParameters are not disposed.

Any insights on this?

Thanks

NLV

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

揽月 2024-09-12 04:25:28

我看到这个:

我妥善处理了所有东西。

还有这个:

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

但是,这些是互斥的!如果您直接调用 .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 数据加载到内存中,这样它就永远不会进入大对象堆。使用文件流或类似的东西代替。

I see this:

i properly dispose everything.

and this:

I've disposed sqlcommand object by calling oSqlCommand.Dispose()

However, those are mutually exclusive! If you call .Dispose() directly, you're doing it wrong. Specifically, you leave open the possibility an exception will make the program skip over the call to the Dispose() method. The "proper" way to dispose of a command creates it with a using block, like so:

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

Now, I gather from the question this isn't the main problem at the moment, but it is a point worth driving home.

As for the question about parameters: SqlParameters do not implement IDisposable. Therefore, you do not dispose them directly. They are an entirely managed resource, and that means they ar ecleaned up by the garbage collector at some point after they are no longer reachable. You don't have to do anything to clean them up yourself.

If you can seriously show that SqlParameter objects are hanging around long after they should, it means you are holding a reference to them somewhere. For example, perhaps you are "caching" old SqlCommand objects somewhere, which in turn hold on to all their parameters. Don't do that. Find and eliminate whatever still references the SqlParameters, and the garbage collector will clean them up for you.

Update:

After re-reading your question, it sounds like the xml parameters end up on the Large Object Heap. The garbage collector in .Net is generational – it doesn't clean up everything every time it runs. As an object moves to a higher generation, it's more likely to hang around a while. The Large Object Heap is basically the last generation, and it does not get cleaned up much at all. More than that, it does not get compacted ever, such that over time it fragments. This can cause a program to hold on to much more data than it needs. What you need to do is try to find a way to keep from loading the entire xml data for a parameter into memory, such that it never makes it to the large object heap. Use a file stream, or something similar instead.

轻拂→两袖风尘 2024-09-12 04:25:28

由于 SqlParameter 不是 IDisposable,因此这不是处置它的问题;而是处理它的问题。 通常整理引用等没有什么好处,因为它仍然受到相同的GC的影响。

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

Since SqlParameter is not IDisposable, it isn't an issue of disposing it; and normally there would be little benefit in tidying up the references etc, since it is still subject to the same GC.

If sounds like you have accidentally kept a reference to the SqlCommand. But if you are sure that you are done, you could try explicitly setting each .Value to null, and calling Clear() on the parameters list. But that is really just masking the fact that you are clinging onto a dead command.

羁绊已千年 2024-09-12 04:25:28

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

the Dispose does not Dispose it's parameters, only disposes it's internal SqlMetaData cache... it's btw normal that the parameters are not disposed automatically because you could pass in something that should not be disposed after disposing the command... + SqlParameter is not implementing Dispose either because it holds no unmanaged resources ....

臻嫒无言 2024-09-12 04:25:28

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

Without testing this I can think of two things that might help you. With the SqlParameters you could use the finalize() method which will free up resources. Also are you running all of your Sql commands through a using block? If so when the using block is finished your resources should be reclaimed and it will remove your memory leak issues.

痴情换悲伤 2024-09-12 04:25:28

我已经在几个项目中使用了这种模式,没有任何问题

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

I have used this pattern is several projects without any issues

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();
        }
    }
}

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

痴骨ら 2024-09-12 04:25:28

如果您确信是 SqlParameter 保留了最后一个引用,则可以执行多种操作。

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

其次,创建您自己的 SqlParameter-s,将它们保存在字典中,然后执行类似的操作:

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

然后在完成命令运行并处置命令并关闭(如果仍然打开)并处置连接后,进入您的字典,显式分配null as SqlParameter.Value(或者,将 .Value 中的字符串引用放入本地变量,将 String.Empty 分配给 .Value,然后将 null 分配给本地变量 - 仅当 SqlParameter.Value 抱怨直接为 null 时才会这样做。然后分配null 到字典项(这是对 SqlParameter 的引用),然后将 null 分配给字典。

在一种更简单的情况下,您可以仅保留对那个关键 SqlParameter 的引用并跳过字典关键是保留。显式分配 null - 字符串的最后一个引用,然后是包含该字符串的 SqlParameter 的最后一个引用。

请记住,这涉及到一些事情,首先是根本不解析中间层的 XML - 只是发送。它深入到 SQL 并以显式无效引用结束。如果您的代码实际上是动态构建 XML,请将其作为一个大的直字符串来尝试。

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

另外,由于您没有说明数据实际上有多大以及您运行的硬件类型,并且如果您的中间层在 IIS 下运行,则很难推测可能的进一步选项,例如让 IIS 运行多个工作进程并回收当它们变得太臃肿时。对于真正巨大的内存消耗和真正的直通中间层(意味着没有缓存构建),这可能比摆弄 gc 更快,但我们正在讨论真正巨大的数据才能进入该区域。

There are several things you can do if you are positive that it's the SqlParameter that's keeping the last reference.

First, try to pass your XML as a string (and use OPENXML in a sproc to handle it) just to see if having a simple object and more control will help.

Second, make your own SqlParameter-s, keep them in a Dictionary and then do something like:

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

Then after you finish the command run and dispose the command and close (if still open) and dispose the connection, go into your dictionary, explicitly assign null as SqlParameter.Value (or, take the string ref from .Value into a local var, assign String.Empty to .Value and then assign null to local var -- this is only if SqlParameter.Value complains about straight null. Then assign null to the dictionary item (that was a ref to SqlParameter) and then assign null to dictionary.

In a more simple case you can keep a ref just to that one critical SqlParameter and skip the dictionary. The key point is to keep explicitly assigning nulls - the the last ref to the string and then to the last ref to SqlParameter that contained it.

Remember that there are several things involved. It starts with not parsing XML in mid-tier at all - just sending it down to SQL and ends with explicitly nullifying references. If your code is such that it's actually constructing that XML on the fly, make one as a large straight string to try.

If this alone doesn't lower the memory pressure then you'll have to force explicit GC collections but for that you have to do abit of the reading and plan reasonable intervals since GC costs a lot i.e. if you start GC-in after every request like a mad rabbit you are going to pay a lot in CPU cycles.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文