在终结器中分配的资源与在dispose中释放的资源有什么区别

时间:2019-01-22 08:30:56

标签: c# idisposable finalizer

这是该问题的后续问题:

Finalize/Dispose pattern in C#

所以我知道,如果我要创建一个使用非托管资源的类,则应该将其处置。链接问题中的答案表示终结器处理了非托管资源。但是,Dispose(Boolean)方法也可以处理非托管资源:

protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    } 

那么,终结器的处置与dispose方法的处置有什么区别?

4 个答案:

答案 0 :(得分:3)

您会使用它的唯一原因(及其极具争议性)。

  1. 终结器允许清除对象,然后再由垃圾回收器将其删除。 (也就是说,GC负责调用它,并从内存中清除对象。)如果开发人员忘记调用对象的Dispose()方法,则可以释放非托管资源,因此避免了泄漏。

有很多理由不这样做,而且有很多方法可以弄错它。简而言之,很少有您需要这样做或想要这样做的原因

答案 1 :(得分:1)

除了给出的答案:终结器在运行时由垃圾收集器调用。

因此,您不能依赖在finalizer中释放不受管资源的时间!因为不知道。

另外,终结器在另一个线程上运行,因此当垃圾回收完成时,终结器可能仍在运行!因此必须通过另一个垃圾回收来完全摆脱对象。

因此,第一个垃圾回收会调用finalezrs,但是不会收集对象(还有对象所引用的对象),它将在第二个垃圾回收中收集。

答案 2 :(得分:0)

带有终结器的对象经历GC的两个阶段:第一次运行终结器,第二次,实际上收集了对象并释放了内存。除了增加GC压力并延迟将内存释放回池之外,终结器还具有处理其字段可能处于无效状态的对象的功能。另外,在终结器线程上引发异常会立即破坏整个应用程序,而没有任何关于发生了什么的友好信息。

这就是为什么Dispose模式实现始终以对GC.SuppressFinalize的调用为特征的原因,这会导致终结器在对象已被处置且GC可以在第一次运行时直接释放内存的情况下不运行。

通常,如果您的应用程序应能够承受严重异常(例如内存不足或线程中止以及随后的AppDomain卸载),则使用终结器可能会非常复杂和棘手。SQLp或IIS等应用程序就是这种情况。

长话短说:除非绝对必要,否则不要使用终结器,并且如果必须这样做(例如,使用非托管资源),则有很多研究在等待着您。

您可以在以下博客文章中找到有关此主题的更多阅读内容:

Eric Lippert - When everything you know is wrong

Joe Duffy - Never write a finalizer again

答案 3 :(得分:0)

GC在对象被收集之前的调用~finalizer

这意味着对象的托管成员也将被收集或已经收集(我不知道GC如何工作的细微差别)。

因此,不必清理托管成员,通常在Dispose(false)中有~finalizer来阻止它。

~B()
{
    Dispose(false);
}

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        // get rid of managed resources
    }   
    // get rid of unmanaged resources
}

当我们通过调用Dispose()方法或使用using手动处置对象时,应清理对象的成员并准备进行收集(将值设置为null等)。 因此,我们在Dispose(true)方法中使用了Dispose(),并且GC.SuppressFinalize(this);禁用了~finalizer调用,因为在对象成员已经清除之后不必调用Dispose(bool disposing),这是没有必要的两次。

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}