你是否需要处理对象并将它们设置为null,或者当垃圾收集器超出范围时它们是否会将它们清理干净?
答案 0 :(得分:221)
当不再使用对象以及垃圾收集器认为合适时,将清理对象。有时,您可能需要将对象设置为null
以使其超出范围(例如您不再需要其值的静态字段),但总体而言通常无需设置为{{ 1}}。
关于处置对象,我同意@Andre。如果对象是null
,那么当您不再需要它时,是一个好主意,特别是如果对象使用非托管资源。不处理非托管资源会导致导致内存泄漏。
一旦程序离开IDisposable
语句的范围,您就可以使用using
语句自动处理对象。
using
在功能上等同于:
using (MyIDisposableObject obj = new MyIDisposableObject())
{
// use the object here
} // the object is disposed here
答案 1 :(得分:129)
答案 2 :(得分:17)
正如其他人所说,如果该类实现Dispose
,你肯定想要调用IDisposable
。我对此采取了相当严格的立场。例如,有些人可能会声称在Dispose
上调用DataSet
毫无意义,因为他们反汇编它并发现它没有做任何有意义的事情。但是,我认为这个论点中存在很多谬论。
阅读this,由受尊敬的个人就此问题进行有趣的辩论。然后阅读我的推理here为什么我认为Jeffery Richter在错误的阵营中。
现在,您是否应该设置对null
的引用。答案是不。让我用以下代码说明我的观点。
public static void Main()
{
Object a = new Object();
Console.WriteLine("object created");
DoSomething(a);
Console.WriteLine("object used");
a = null;
Console.WriteLine("reference set to null");
}
那么您认为a
引用的对象何时符合收集条件?如果您在致电a = null
之后说过,那么您错了。如果您在Main
方法完成之后说过,那么您也错了。正确的答案是,在<{>>期间 期间,它有资格收集。DoSomething
。那是对的。该参考设置为null
,甚至可能在DoSomething
的调用完成之前 。这是因为JIT编译器可以识别对象引用何时不再解除引用,即使它们仍然是有根的。
答案 3 :(得分:13)
您永远不需要在C#中将对象设置为null。编译器和运行时将负责确定它们何时不在范围内。
是的,你应该处理实现IDisposable的对象。
答案 4 :(得分:11)
如果对象实现IDisposable
,那么是的,你应该处理它。该对象可能会挂起到本机资源(文件句柄,OS对象),否则可能无法立即释放。这可能导致资源匮乏,文件锁定问题以及其他可以避免的微妙错误。
另请参阅MSDN上的Implementing a Dispose Method。
答案 5 :(得分:11)
我同意这里的常见答案,是的,你应该处置,并且通常不应该将变量设置为null ...但我想指出dispose主要不是关于内存管理。是的,它可以帮助(有时确实)内存管理,但它的主要目的是为您提供确定性的稀缺资源发布。
例如,如果您打开硬件端口(例如串行),TCP / IP套接字,文件(独占访问模式)甚至数据库连接,您现在已阻止任何其他代码使用这些项目,直到它们为止被释放。处理通常会释放这些物品(以及GDI和其他“os”手柄等,其中有1000种可用,但总体上仍然有限)。如果您没有在所有者对象上调用dipose并显式释放这些资源,那么尝试将来再次打开相同的资源(或另一个程序),打开尝试将失败,因为未处理的未收集对象仍然打开该项目。当然,当GC收集项目时(如果Dispose模式已经正确实现),资源将被释放...但你不知道什么时候会发布,所以你不知道何时可以安全打开那个资源。这是Dispose解决的主要问题。当然,释放这些句柄通常也会释放内存,永远不会释放它们可能永远不会释放内存...因此所有关于内存泄漏或内存延迟清理的讨论。
我已经看到了这个导致问题的现实世界的例子。例如,我看到ASP.Net Web应用程序最终无法连接到数据库(尽管很短的时间,或者直到Web服务器进程重新启动),因为sql server的“连接池已满”...即,如此多的连接已经创建,并且在如此短的时间内没有明确释放,因此无法创建新的连接,并且池中的许多连接虽然不活动,但仍然被未填充和未收集的对象引用,因此可以'重复使用。在必要时正确处理数据库连接可确保不会发生此问题(至少除非您具有非常高并发访问权限)。
答案 6 :(得分:9)
如果他们实现了IDisposable接口,那么你应该处理它们。垃圾收集器将负责其余的工作。
编辑:最好是在处理一次性物品时使用using
命令:
using(var con = new SqlConnection("..")){ ...
答案 7 :(得分:4)
通常,不需要将字段设置为null。但我总是建议处理非托管资源。
根据经验,我还建议您执行以下操作:
我遇到一些非常难以找到的问题,这些问题是不遵循上述建议的直接结果。
这样做的好地方是Dispose(),但通常会更快。
通常,如果对象存在引用,则垃圾收集器(GC)可能需要更长的时间才能确定对象不再使用。对象一直留在记忆中。
在您发现应用程序使用的内存比预期的要多得多之前,这可能不是问题。发生这种情况时,请连接内存分析器以查看哪些对象未被清除。将引用其他对象的字段设置为null并清除处理集合可以真正帮助GC确定它可以从内存中删除哪些对象。 GC将更快地回收使用的内存,使您的应用程序的内存消耗更少,速度更快。
答案 8 :(得分:3)
始终致电处置。这不值得冒风险。应尊重大型托管企业应用程序。不能做任何假设,否则它会回来咬你。
不要听leppie。
许多对象实际上并没有实现IDisposable,因此您不必担心它们。如果它们真的超出范围,它们将自动释放。此外,我从未遇到过必须将某些内容设置为null的情况。
可能发生的一件事是许多物品可以被打开。这可以大大增加应用程序的内存使用量。有时很难弄清楚这实际上是内存泄漏,还是你的应用程序只是在做很多事情。
内存配置文件工具可以帮助解决这类问题,但这可能很棘手。
此外,始终取消订阅不需要的活动。还要小心WPF绑定和控件。不是通常的情况,但我遇到了一种情况,我有一个绑定到底层对象的WPF控件。底层对象很大,占用了大量内存。 WPF控件正在被一个新实例取代,旧的实例仍然因为某些原因而闲置。这导致了大量的内存泄漏。
在hindsite中,代码编写得很糟糕,但重点是你要确保未使用的代码超出范围。那个花了很长时间才找到内存分析器,因为很难知道内存中的东西是有效的,什么不应该存在。
答案 9 :(得分:3)
当一个对象实现IDisposable
时,你应该调用Dispose
(或Close
,在某些情况下,会为你调用Dispose)。
您通常不必将对象设置为null
,因为GC将知道不再使用对象。
将对象设置为null
时有一个例外。当我检索需要处理的大量对象(来自数据库)时,将它们存储在集合(或数组)中。当“工作”完成后,我将对象设置为null
,因为GC不知道我已经完成了它。
示例:
using (var db = GetDatabase()) {
// Retrieves array of keys
var keys = db.GetRecords(mySelection);
for(int i = 0; i < keys.Length; i++) {
var record = db.GetRecord(keys[i]);
record.DoWork();
keys[i] = null; // GC can dispose of key now
// The record had gone out of scope automatically,
// and does not need any special treatment
}
} // end using => db.Dispose is called
答案 10 :(得分:2)
我也要回答。 JIT生成表以及来自变量使用的静态分析的代码。 这些表条目是当前堆栈帧中的“GC-Roots”。随着指令指针的前进,这些表条目变得无效,因此可以进行垃圾收集。 因此:如果它是作用域变量,则不需要将其设置为null - GC将收集该对象。 如果它是成员或静态变量,则必须将其设置为null
答案 11 :(得分:0)
在这一集.NET Rocks中,对这个主题进行了很好的讨论(以及Dispose模式背后的历史)!