在.NET中,什么情况下应该使用GC.SuppressFinalize()
?
使用这种方法有哪些优势?
在.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
IDisposable
接口的类没有声明为 sealed
,那么即使它不包括自定义终结器,也应该包含调用 GC.SuppressFinalize(this)
的代码。这是为了确保对于添加自定义终结器但仅覆盖受保护的 Dispose(bool)
方法的派生类型具有正确的语义。 - Sam HarwellSupressFinalize
告诉系统,不需要调用终结器因为在终结器中需要完成的工作已经完成。从.NET文档中得知:
实现IDisposable接口的对象可以在IDisposable.Dispose方法中调用此方法,以防止垃圾回收器在不需要它的对象上调用Object.Finalize。
通常情况下,大多数的Dispose()
方法都应该能够调用GC.SupressFinalize()
,因为它应该清理掉终结器中需要清理的所有内容。
SupressFinalize
仅提供了一种优化方式,允许系统不必将对象排队到终结器线程中。一个正确编写的Dispose()
和终结器应该能够正常工作,无论是否调用了GC.SupressFinalize()
。
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
方法中调用,这样如果有人调用Dispose
方法,GC就不会再次调用终结器。