在.NET中,什么情况下应该使用
GC.SuppressFinalize()
?
使用这种方法给我带来什么好处?
SuppressFinalize
只能由具有终结器的类调用。它通知垃圾收集器 (GC) this
对象已完全清理。
当您有终结器时,推荐的
IDisposable
模式是:
public class MyClass : IDisposable
{
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// called via myClass.Dispose().
// OK to use any private object references
}
// Release unmanaged resources.
// Set large fields to null.
disposed = true;
}
}
public void Dispose() // Implement IDisposable
{
Dispose(true);
GC.SuppressFinalize(this);
}
~MyClass() // the finalizer
{
Dispose(false);
}
}
通常,CLR 在创建对象时会使用终结器来监视对象(这使得创建它们的成本更高)。
SuppressFinalize
告诉 GC 该对象已正确清理,不需要进入终结器队列。它看起来像一个 C++ 析构函数,但其行为却一点也不像。
SuppressFinalize
优化并非微不足道,因为您的对象可以在终结器队列上等待很长时间。不要试图在其他提醒您的对象上拨打 SuppressFinalize
。这是一个即将发生的严重缺陷。
设计指南告诉我们,如果您的对象实现了
IDisposable
,则不需要终结器,但如果您有终结器,则应该实现 IDisposable
以允许对类进行确定性清理。
大多数时候,您应该能够使用
IDisposable
来清理资源。仅当您的对象保留非托管资源并且需要保证这些资源被清理时,您才应该需要终结器。
注意:有时,程序员会添加终结器来调试自己的
IDisposable
类的构建,以测试代码是否已正确处理其 IDisposable
对象。
public void Dispose() // Implement IDisposable
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
#if DEBUG
~MyClass() // the finalizer
{
Dispose(false);
}
#endif
SuppressFinalize
告诉系统在终结器中应该完成的所有工作都已经完成,因此不需要调用终结器。 来自 .NET 文档:
实现 IDisposable 的对象 接口可以调用这个方法 IDisposable.Dispose 方法 防止垃圾收集器 调用 Object.Finalize 不需要它的对象。
一般来说,大多数
Dispose()
方法都应该能够调用 GC.SuppressFinalize()
,因为它应该清理将在终结器中清理的所有内容。
SuppressFinalize
只是提供了一种优化,允许系统不必费心将对象排队到终结器线程。无论是否调用 Dispose()
,正确编写的 GC.SuppressFinalize()
/终结器都应该正常工作。
Dispose(true);
GC.SuppressFinalize(this);
如果对象有终结器,.net 将引用放入终结队列中。
由于我们调用了
Dispose(true)
,它清除了对象,所以我们不需要终结队列来完成这项工作。
因此调用
GC.SuppressFinalize(this)
删除终结队列中的引用。
如果一个类或从它派生的任何东西可能保存对具有终结器的对象的最后一个实时引用,则在可能受到该终结器不利影响的任何操作之后,应在该对象上调用
GC.SuppressFinalize(this)
或 GC.KeepAlive(this)
,从而确保终结器在该操作完成之前不会运行。
GC.KeepAlive()
和GC.SuppressFinalize(this)
的成本在任何没有终结器的类中本质上是相同的,并且有终结器的类通常应该调用GC.SuppressFinalize(this)
,因此使用后一个函数作为的最后一步Dispose()
可能并不总是必要的,但它不会错。
我反对已接受的答案并说:从不。
当您有非托管资源需要释放时,请单独使用
IDisposable.Dispose()
。
不要直接使用 Finalizers 或 GC。
实施微软推荐的 Disposable-Pattern 是一个(在我看来)错误,因为它会增加 Disposal 的复杂性。
确实,未能调用 Dispose() 会泄漏资源,但这将违反合同
Dispose()
在终结器队列中拥有已处理的对象不会降低性能。
该方法必须在实现
Dispose
的对象的 IDisposable
方法上调用,这样,如果有人调用 Dispose
方法,GC 就不会再次调用终结器。