首页 文章

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

提问于
浏览
0

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

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

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

例如;

我以this帖子为例

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

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

Managed resource: 任何实现IDisposable接口的.NET类,如Streams和DbConnections .

Unmanaged resource: 包装在Managed Resource类中的填充 . 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 回答

  • 1

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

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

    你有一个对象,它代表一个资源(无论是连接,句柄,任何东西,管理与否) . 设_127501_ s IDisposable 也有终结器 .

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

    • 如果你调用 Dispose 方法,你就完成了它,并指示它释放它所拥有的任何资源(销毁句柄,关闭连接等) .

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

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

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

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

    可以把它想象成一个 failsafe 机制 . GC执行此脏操作的事实可以避免内存/资源泄漏,因为您的资源最终会被释放 . 但为什么要等待并对系统施加压力?

    现在,您需要释放托管对象,因为GC可以处理这些问题 . 这是传递性的:如果你有一个托管对象链,最后一个托管到非托管引用,只有最后一个需要特别小心,GC最终会处理它 .

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


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

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

    正确 .

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

    绝对不 . 在这种情况下,在一个可疑的环境中将调用终结器 . 不会调用 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) 方法在这里被终结时被调用,但是不要把它与 IDisposable.Dispose 这个混淆 .

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

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

相关问题