我有以下代码:
public void Dispose()
{
if (_instance != null)
{
_instance = null;
// Call GC.SupressFinalize to take this object off the finalization
// queue and prevent finalization code for this object from
// executing a second time.
GC.SuppressFinalize(this);
}
}
虽然有一条评论解释了与GC相关的电话的目的,但我仍然不明白为什么会这样。
一旦所有实例都停止存在,对象是不是用于垃圾收集的对象,例如,在using
块中使用时?
这会起到重要作用的用例场景是什么?
答案 0 :(得分:33)
在实施配置模式时,您还可以向调用Dispose()
的类添加终结器。这是为了确保Dispose()
总是被调用,即使客户忘记调用它。
要防止dispose方法运行两次(如果已经处置了对象),则添加GC.SuppressFinalize(this);
。该文档提供了sample:
class MyResource : IDisposable
{
[...]
// This destructor will run only if the Dispose method
// does not get called.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
resource.Cleanup()
}
disposed = true;
}
}
答案 1 :(得分:31)
垃圾收集:GC不再引用对象时回收对象使用的内存。
Dispose :IDisposable接口中的一个方法,用于在程序员调用它时(直接或间接通过using块)释放所有托管和非托管资源。
Finalizer :释放所有非托管资源的方法。在回收内存之前由GC调用。
托管资源:任何实现IDisposable
接口的.NET类,如Streams和DbConnections。
非托管资源:托管资源类中包含的填充。 Windows句柄是最简单的例子。
现在回答你的问题:
GC保留所有对象的列表(Finalization Queue),其中的类声明了Finalizer(在C#中为~ClassName)。对象在创建时放入此队列中。 GC定期运行以检查程序中是否有任何对象无法访问。然后,它检查是否从Finalization Queue引用了任何不可访问的对象,并将它们放在另一个名为Freacheable队列的队列中,而其余的则被回收。一个单独的线程用于运行Freacheable队列中对象的Finalize方法。
GC下次运行时,会发现之前Freacheable队列中的一些对象已经完成,因此可以进行回收。请注意,GC需要至少两个周期(如果要进行大量的Finalization,则需要更多的周期)来摆脱具有Finalizer的对象,这会导致一些性能损失。
SuppressFinalize
方法只是在对象标题中设置一个标志,表示不必运行Finalizer。这样GC就可以立即回收对象的内存。根据上面的定义,Dispose
方法与Finalizer(以及更多)执行相同的操作,因此如果执行它,则不再需要Finalization。使用SuppressFinalize
方法,您可以通过通知它来保存GC的一些工作。此外,现在您不必在Finalizer中实施检查以避免双重释放。 Dispose
唯一的问题是无法保证运行,因为程序员有责任调用它,这就是为什么有时我们需要打扰终结器。
话虽如此,你很少需要编写Finalizer,因为对于绝大多数通常的非托管资源,托管包装器已经存在,并且托管资源将通过调用它们{{1}来释放来自您自己的Dispose
方法的方法,仅来自那里!在终结器中,您不能调用Dispose方法。
进一步阅读:
答案 2 :(得分:6)
可以在第一次GC运行时完成的对象。
通常,当GC检测到某个对象无法访问时,它会回收它。如果对象可以最终确定,那么GC不会回收它;相反,它认为它仍然可以访问(以及该对象引用的所有对象,等等),并安排它进行最终化。只有在再次再次在完成后的某个时刻无法访问时,才会回收该对象。
这意味着可终结对象会产生额外成本:对象必须在内存中保留较长时间。因此,您看到了这样的呼叫:在不需要时禁止终结是值得的。这里,对象使用finalization来确保它总是在某个时刻被“处理掉”。明确处理时,不再需要最终确定。
答案 3 :(得分:2)
如果你的类型实现了终结器(~MyType() { }
),它会阻止垃圾收集器运行它。在终结器处理非托管类型时使用,但用户已经调用Dispose()
(显式或通过using() { }
块),释放那些非托管类型。
答案 4 :(得分:0)
来自MSDN:GC.SuppressFinalize:
此方法在对象中设置一个位 标头,系统检查何时 呼叫终结者。 obj参数 必须是这个的来电者 方法
实现IDisposable的对象 接口可以调用此方法 IDisposable .. ::。Dispose方法 防止垃圾收集器 调用Object .. ::。Finalize on a 不需要它的对象。
如果你的对象没有引用其他对象,只是离散类型,或者已经将任何对象引用重置为NULL,通常你会使用它。