何时会处理方法不被调用?
我前几天在读this文章,并想知道为什么有一个Finalizer和Dispose方法。我在SO上阅读here以了解为什么您可能需要将Dispose添加到Finalizer。我的好奇心是,Finalizer什么时候会被调用Dispose方法?有代码示例还是基于软件正在运行的系统上发生的事情?如果是这样,那么可能会发生没有GC运行的Dispose方法。何时会处理方法不被调用?
这里的终结者的目的仅仅是防止内存泄漏的安全预防措施(如果您发生而不是明确呼叫Dispose
)。这也意味着如果您希望他们在程序关闭时释放资源,则无需处理对象,因为无论如何GC都将被强制完成并收集所有对象。
作为一个相关点,从终结者那里处理对象略有不同是很重要的。
~MyClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(disposing)
{
if (!this.disposed)
{
if (disposing)
{
// Dispose managed resources here.
}
// Dispose unmanaged resources here.
}
this.disposed = true;
}
的原因,你不希望在您的finaliser处置管理资源,你实际上这样做可以创建他们强引用,这可能阻止GC做的工作正常和收集他们。当然,非托管资源(例如Win32句柄等)应该总是显式关闭/处置,因为CLR不知道它们。
这主要是为了保护自己。你无法指定你班级的最终用户会做什么。除了Dispose方法之外,GC还会“Dispose”你的对象,即使用户忘记调用Dispose()或错误地使用你的类,也会适当地释放你的资源。
值得一提的是,GC是非确定性的,因此不能保证何时,甚至是否会调用您的finaliser。 – LukeH 2009-09-10 13:40:36
是的 - 如果你的程序运行时间足够长,你的对象最有可能被敲定。另外,如果它关闭干净,它会得到最终确定。但GC没有保证 - 这是IDisposable首先存在的一部分。 – 2009-09-10 13:56:31
必须显式调用dispose方法,方法是调用Dispose()或使用using语句中的对象。 GC将始终调用终结器,因此如果在对象处理完终结器之前有某些事情需要发生,至少应该检查以确保对象中的所有内容都已清理完毕。
如果可能的话,您希望避免清理终结器中的对象,因为与之前处理它们相比,它会导致额外的工作(比如调用dispose),但是至少应该检查终结器中是否有对象躺在需要被删除的周围。
终止器在对象被垃圾收集时调用。 Dispose需要显式调用。在下面的代码中,终结器将被调用,但Dispose方法不会。
class Foo : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed");
}
~Foo()
{
Console.WriteLine("Finalized");
}
}
...
public void Go()
{
Foo foo = new Foo();
}
这并非完全正确。在对象有资格进行垃圾收集之后(即应用程序不再引用实例),终结器会被调用一段时间。但是,由于必须为实例运行终结器,因此CLR实际上是对象的根源,因此在终结器运行之前不会收集垃圾。 – 2009-09-10 11:48:38
也不能保证一个对象将被GC'd或者它的终结器永远被调用。这就是为什么确保你正确处理任何'IDisposable'对象这一点非常重要。 – LukeH 2009-09-10 13:27:18
尚未提及的一个重要但微妙的注意事项:Dispose很少考虑的目的是防止过早清理对象。带终结器的对象必须仔细书写,以免终止运行早于比预期。在最后一次方法调用开始之前,终结器无法运行,该方法将在对象(*)上进行,但如果对象在方法完成后将被放弃,则在最后一次方法调用期间,它有时可能会运行。正确处理对象的代码在调用Dispose之前不能放弃对象,因此不会有终结器对正确使用Dispose的代码造成严重破坏的危险。另一方面,如果使用对象的最后一个方法利用了在最后一次使用对象引用本身后在终结器中清理的实体,那么垃圾收集器可以调用对象的Finalize并清理这些实体仍在使用中。补救措施是确保使用实体的任何调用方法必须在某个时刻通过使用“this”的方法调用来遵循。 GC.KeepAlive(this)是一个很好的方法。 (*)扩展为不对对象执行任何操作的内联代码的非虚方法可免于此规则,但Dispose通常是或调用虚拟方法。
另一个不会在终结者中处理托管资源的原因......可能在您的终结者执行时他们可能已经GC'd了。尝试在收集它们时收集它们会导致运行时错误。 – LukeH 2009-09-10 13:33:10
@Luke:没错,但是可以通过将所有引用设置为null,然后在处理之前执行空检查来轻松避免。 – Noldorin 2009-09-10 13:49:33
@Noldorin - 在你的例子中哪里会注销事件?我理解技术上他们会受到管理,但是如果我们通过某个事件绑定了一些与此类相关的对象,并且我们没有在非托管部分取消注册它(假设用户不直接调用Dispose并且它的左边直到GC来清理它向上)。将事件取消注册到托管部分以确保发生这种情况是否安全?副作用可能是有人认为他们正在处理一个对象,但实际上它从来不会因为这个类和其他类之间的事件链接而被取消。 – SwDevMan81 2009-09-13 00:27:09