关于c#:GC如何管理任何类似于streamwriter的类/对象,如果调用Dispose方法,默认情况下默认实现IDisposable?

How GC manages any class/object like streamwriter which implement IDisposable by default in case of calling Dispose method or not ?

我读过一些关于gc、终结器、托管&非托管对象、一次性模式@stackoverflow的内容。

目前,我对GC、终结器、可释放模式和托管的、非托管的资源术语的正确用法感到非常困惑。

我知道上面提到的主题有很多误导性的答案。

例如;

我以这篇文章为例

这个问题的公认答案意味着,如果我们不调用实现IDisposable接口的.NET对象的Dispose方法,那么我们将无法释放非托管资源。

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

托管资源:实现IDisposable接口的任何.NET类,如流和数据库连接。

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

如本帖所述

因此,我最终想到,默认情况下实现IDisposable接口的所有类(如pen、streamwriter)都是托管资源,这些资源在内部包含非托管资源。

显式调用IDispose方法和让GC这样做之间的所有区别是,一个间接地向GC发出信号,表示对象可以在下一个GC期间被清除,后者完全不确定。

但是,当我看到DataSet和DataTable类时,尽管它们默认实现了IDisposable,但它们在内部不拥有任何非托管资源。这个公认的答案也支持我的想法。

引自答案

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

所以我的第一个想法失败了。默认情况下拥有一次性的并不一定意味着类/对象内部有非托管资源。

Q1)这是真的吗?

q2)如果我使用一个类似streamwriter的类,它默认包含IDisposable接口,并且不调用它,那么gc会将它放入finalization队列,然后分别调用dispose方法吗?(我的意思是streamwriter.dispose()方法,相当于streamwriter.close())

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


我认为,在这里,您试图区分托管资源和非托管资源会让您感到困惑。

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

您有一个对象,它表示一个资源(无论是连接、句柄、任何东西、托管的还是非托管的)。假设它是IDisposable,也有一个终结器。

您在代码中使用了对象,并且在某个时候已经完成了。

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

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

    正确实现的释放模式将在此时将对象标记为不再需要终结。

  • 你忽略了调用Dispose,让对象失去可到达性。在某个时刻,GC发现它,GC将对象放入终结器队列,终结器运行并释放资源(稍后将详细介绍),您的对象返回GC,然后释放。

    这大约需要一个世纪。在这段时间内,您有一个未引用的对象,它毫无理由地保存着一些资源。通过强制GC完成对象,对它施加压力。

    把这看作是一种故障保护机制。GC做这些脏工作的事实允许您避免内存/资源泄漏,因为您的资源最终将被释放。但是为什么要等着给系统施加压力呢?

现在,您将了解到终结器是用来释放非托管资源的,这是正确的。为什么会这样?您不需要释放托管对象,因为GC可以处理这些对象。这是可传递的:如果您有一个托管对象链,最后一个对象保持在非托管引用上,那么只有最后一个对象需要特别注意,并且GC将处理它……最终。

有时一次性模式用于在超出using范围时执行副作用,无论是正常情况下还是通过异常传播。这是一种很好的技术,用于确定性清理。但你不能依赖终结器来执行这样的副作用。首先,它在终结器线程中被非确定性地称为,更糟的是:您不能对它运行的环境进行任何假设。这就是为什么您应该只执行代码来避免内存泄漏和释放终结器中的非托管资源的原因。在定稿器中做最少的工作。

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

Having a Disposable by default doesn't necessarily mean that class/object has unmanaged resources inside. Is it true?

对的。

If I use a class like streamwriter which includes IDisposable interface by default and not call it.Will GC put it Finalization queue and then call Dispose method respectively ?(I mean StreamWriter.Dispose() method which is equivalent to StreamWriter.Close()

绝对不是。在这种情况下,将在可疑的环境中调用终结器。不会调用IDisposable.Dispose方法。

但有一个非常常见的模式,它是以下内容的变体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)方法,但不要把它与IDisposable.Dispose方法混淆。

Is finalization queue takes place if we explicitly implement destructor ?

是的,如果对象声明了析构函数,即终结器,那么它将被放置在终结队列上。我不喜欢析构函数项,因为它与C++有着非常不同的含义,最好把它称为终结器,所以差异是突出的。