eto*_*bot 5 .net c# garbage-collection idisposable
如果我有这样的嵌套对象的代码,我是否需要使用嵌套的using语句来确保SQLCommand和SQLConnection对象都正确处理,如下所示,或者如果实例化SQLCommand的代码在外部使用声明.
using (var conn = new SqlConnection(sqlConnString))
{
using (var cmd = new SqlCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandText = cmdTextHere;
conn.Open();
cmd.Connection = conn;
rowsAffected = cmd.ExecuteNonQuery();
}
}
Run Code Online (Sandbox Code Playgroud)
是.你可以像这样清理一下
using (SqlConnection conn = new SqlConnection(sqlConnString))
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand())
{
// code here
}
Run Code Online (Sandbox Code Playgroud)
但是你仍然希望using
每个IDisposable对象都有一个.
编辑:考虑这个不使用内部using
语句的例子.
class A : IDisposable
{
public void Dispose()
{
Console.WriteLine("A Disposed");
}
}
class B : IDisposable
{
public void Dispose()
{
Console.WriteLine("B Disposed");
}
}
Run Code Online (Sandbox Code Playgroud)
码
using (A a = new A())
{
B b = new B();
}
Run Code Online (Sandbox Code Playgroud)
在块的末尾,A被妥善处理.B怎么了?嗯,它超出范围,只是等待垃圾收集.B.Dispose()没有被调用.另一方面
using (A a = new A())
using (B b = new B())
{
}
Run Code Online (Sandbox Code Playgroud)
当执行离开块(实际上是块)时,编译的代码执行对每个对象的Dispose()方法的调用.
您可以省略 周围的使用SqlCommand
。GC 最终会为您清理它。但是,我强烈建议您不要这样做。我会解释原因。
SqlCommand
间接继承自System.ComponentModel.Component
,因此它继承了它的Finalizer
方法。不在 a 上调用 disposeSqlCommand
将确保该命令在超出范围后至少提升一代(.NET 垃圾收集器是分代 gc)。例如:当命令位于第 1 代时,它将移至第 2 代。可终结的对象在内存中保留更长时间,以确保终结器可以安全运行。但不仅命令本身保存在内存中,而且它引用的所有对象也随之保留到该代。它将引用的对象是SqlConnection
、对象列表SqlParameter
、可能较大的CommandText
字符串以及它引用的许多其他内部对象。只有当该代被收集时才能删除该内存,但代数越高,清除的频率就越低。
因此,不调用会给终结器线程带来额外的内存压力和额外的工作。
\n\n当.NET无法分配新内存时,CLR将强制对所有代进行垃圾收集。此后,运行时通常将再次有足够的空间来分配新对象。然而,当内存中有很多对象仍然需要提升到下一代(因为它们是可终结的,或者被可终结的对象引用)时发生这种强制收集时,CLR 可能无法释放内存够了。这OutOfMemoryException
将是结果。
我必须承认我从未见过这种情况发生,因为开发人员并没有\xe2\x80\x99 只处理他们的SqlCommand
对象。然而,我在生产系统中经常看到 OOM,这是由于未正确处理对象造成的。
我希望这能提供一些有关 GC 如何工作以及不正确处置(可终结)对象的风险的背景知识。我总是处理所有一次性物品。虽然查看 Reflector 可以证明这不一定适用于某种类型,但这种编程会导致代码的可维护性较差,并使代码依赖于类型的内部行为(并且这种行为将来可能会改变)。
\n