C#-C# Dispose模式详细分析

Posted 叮呤咣啷品

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#-C# Dispose模式详细分析相关的知识,希望对你有一定的参考价值。

  • 目的:

    为了及时释放宝贵的非托管资源和托管资源,并且保证资源在被gc回收的时候可以正确释放资源,同时兼顾执行效率

  • 必须遵循的事实:

1 托管资源释放:
  由另一线程的gc进行释放,当托管的对象没有被引用时,就会在“适当的时候”进行回收
  如果定义了析构函数,回收的时候会调用析构函数(实际执行可能有差别),之后释放对象占用的内存。
  当类有析构函数时, gc会分分两步来释放,如果没有析构函数或者指定不需要调用析构函数时,只需要一步就能释放

2 非托管资源必须显式释放

  • 方案:

1.把资源释放都放在析构函数里。

可以保证资源都释放,但是由于gc调用时机的不确定性,导致宝贵的非托管资源无法及时释放。

2. 写个释放函数,手动是调用

如果忘了释放的话, 托管资源会被gc释放,但非托管资源就无法释放

3. Dispose模式。参考下面的代码

手动调用Dispose() 可以释放所有资源,并且在gc标记不需要再调用析构函数,从而提高了效率。

如果忘记调用Dispose(), 则当gc调用析构函数的时候也会把非托管资源释放掉


参考代码:

public interface IDisposable

{

    void Dispose();

}

public class DisposablClass : IDisposable

{

    //是否回收完毕

    bool _disposed;

    public void Dispose()

    {

        Dispose(true);

        GC.SuppressFinalize(this); //标记gc不在调用析构函数

    }

    ~DisposableClass()

    {

        Dispose(false);

    }


    private void Dispose(bool disposing)

    {

        if(_disposed) return; //如果已经被回收,就中断执行

        if(disposing)

        {

            //TODO:释放本对象中管理的托管资源

        }

        //TODO:释放非托管资源

        _disposed = true;

    }

}


  • 可能存在的疑问

    1. 既然gc是另外一线程执行的,为什么Dispose(bool)函数里不加锁?
     答:因为如果可以主动调用的时候,肯定此对象不是死对象,也不会被回收,因此不会同时调用到

    2. 为什么析构函数调用的dispose(false)不释放托管资源?
        答:因为析构函数由gc来调用,gc会依次释放所有的死对象(不可到达),释放的顺序是随机的,如果在一个对象的析构里调用了一个本次gc已经释放的对象,就会发生释放两次的错误。


以上是关于C#-C# Dispose模式详细分析的主要内容,如果未能解决你的问题,请参考以下文章

C#-C#中的Dispose模式

RxSwift 为啥我们没有调用 dispose 会有内存泄漏

关于 C# Dispose Pattern 的具体问题

C# IDisposable 类,正确使用 Dispose,获取错误“dispose”需要 1 个参数

放在颤振块中

C#DataTable的用法