如果调用Dispose方法,GC如何管理默认情况下实现IDisposable的任何类/对象(如streamwriter)?

时间:2016-04-14 20:27:32

标签: c# garbage-collection dispose idisposable finalizer

我已经阅读了一些有关GC,终结器,管理和放大器的内容。非托管对象,一次性模式@StackOverflow。

目前,我对GC,Finalizers,Disposable模式和托管非托管资源术语的正确用法感到困惑。

恕我直言,关于上述主题有很多误导性的答案。

例如;

我以this帖子为例

这个问题的接受答案意味着如果我们不调用默认情况下实现IDisposable接口的.net对象的dispose方法,我们将无法释放非托管资源。

我认为这是一个错误的陈述。首先,托管和非托管资源之间存在概念混淆。在我看来,

托管资源:任何实现IDisposable接口的.NET类,如Streams和DbConnections。

非托管资源:托管资源类中包含的填充。 Windows句柄是最简单的例子。

正如@kicsit在this帖子所述

所以我最终想到默认情况下所有实现IDisposable接口的类,例如pen,streamwriter都是托管资源, 包括内部的非托管资源。

显式调用IDispose方法和让GC执行操作之间的区别在于间接向GC发出信号,表明在下一次GC期间可以清除对象,后者完全不确定。

但是,当我看一下Dataset和Datatable类时,虽然默认情况下它们实现了IDisposable,但它们并不拥有任何非托管资源。 This接受的回答也支持我的想法。

引用回答

system.data命名空间(ADONET)不包含非托管资源。 因此,只要你没有给自己添加一些特别的东西,就没有必要处置它们。

所以我的第一个想法就是失败。默认情况下,拥有一个Disposable并不一定意味着类/对象里面有非托管资源。

Q1)这是真的吗?

Q2)如果我使用类似streamwriter的类,默认情况下包含IDisposable接口而不调用它.Will GC会把它放到Finalization队列中,然后分别调用Dispose方法吗?(我的意思是StreamWriter.Dispose()方法,它是相当于StreamWriter.Close()

Q3)如果我们显式实现析构函数,是否会发生终结队列?

1 个答案:

答案 0 :(得分:1)

我认为您在此处尝试制作的托管和非托管资源之间的区别让您感到困惑。

让我试着想象这样的事情:

你有一个对象,它代表一个资源(无论是连接,句柄,任何东西,托管与否)。让我们说它是IDisposable并且还有一个终结器。

您在代码中使用该对象,并且在某些时候您完成了它。

  • 如果您调用Dispose方法,您基本上会告诉您已完成的对象,并指示它释放它所拥有的任何资源(销毁句柄,关闭连接等等。)

    这应该是首选的行动方案,最好通过using声明来完成。

    正确实施的配置模式将此对象标记为此时不再需要完成。

  • 您忽略了调用Dispose并让对象失去可达性。在某些时候,它被GC发现,GC将对象放入终结器队列,终结器运行并释放资源(稍后会更多),您的对象返回GC然后被释放。

    大约需要一个世纪。在此期间,您有一个未引用的对象持有一些资源,没有充分的理由。并且你通过强迫它完成对象来对GC施加压力。

    将此视为故障安全机制。 GC执行此脏操作的事实可以避免内存/资源泄漏,因为您的资源最终会被释放。但是为什么要等待并对系统施加压力呢?

现在,您已经读过终结器用于释放非托管资源,这是正确的。这是为什么?您不需要 来发布托管对象,因为GC可以处理这些问题。这是传递性的:如果你有一个托管对象链,最后一个托管对象是非托管引用,那么只有最后一个需要特别小心,GC最终会处理它......

当您离开using范围时,无论是正常情况还是通过异常传播,有时使用一次性模式来执行副作用。这是一种很好的技术,用于确定性清理 但是你不能依靠终结器来执行这样的副作用。首先,它在终结器线程中被称为非确定性,更糟糕的是:you can't assume anything关于它运行的环境。这就是为什么你应该只执行代码来避免内存泄漏和在终结器中释放非托管资源。在终结器中完成最少的工作。

现在,让我们看看你的问题:

  

默认情况下使用Disposable并不一定意味着类/对象内部有非托管资源。这是真的吗?

正确。

  

如果我使用类似streamwriter的类,默认情况下包含IDisposable接口而不调用它.Will GC会把它放到Finalization队列中,然后分别调用Dispose方法吗?(我的意思是StreamWriter.Dispose()方法,相当于StreamWriter.Close ()

绝对不是。在这种情况下,在一个可疑的环境中将调用终结器。 <{1}}方法将被调用。

但是这是一种非常常见的模式,它是以下的变体:

IDisposable.Dispose

我猜你可以说明class Foo : IDisposable { ~Foo() => Dispose(false); public void Dispose() => Dispose(true); protected virtual void Dispose(bool disposing) { if (disposing) { // Do whatever you want to perform deterministically GC.SuppressFinalize(this); // No need to finalize anymore } // Free any unmanaged resource you hold. // Make sure you don't throw here, or kiss your process goodbye. } } 方法在最终确定时会被调用,但不要将其与Dispose(bool)方法混淆。

  

如果我们明确地实现了析构函数,那么是否会发生终结队列?

是的,如果对象声明了析构函数,那么该对象将被放置在终结队列中,AKA是终结器。我不喜欢析构函数这个术语,因为它与C ++的非常含义不同,更好地称之为终结符,因此差异突出。