非托管回收

重要::::首先参考-----C#高级编程  第10版 C# 6 & .NET Core ,次重要的是下面的截图部分

应该了解的垃圾收集机制

对于大多数应用而言,了解垃圾收集机制的主要动机并不是为了对内存“省吃俭用”,而是为了处理非托管资源的控制问题,这些问题往往跟内存的大小没有什么关系。例如对一个文件进行操作,该何时关闭文件,关闭文件时要注意什么问题,如果忘了关闭会带来什么后果?这些都是我们需要认真考虑的,无论你的内存有多大:)

对于这一类的操作,我们不能依赖GC帮我们做,因为它并不知道我们在释放时想干什么,它甚至不知道自己该干什么!我们不得不自己动手来编写处理代码。当然,微软已经为我们搭好了框架,就是这两个函数:Finalize和Dispose。它们也代表了非托管清理的两种方式:自动和手动。

一. Finalize

Finalize很像C++的析构函数,我们在代码中的实现形式为这与C++的析构函数在形式上完全一样,但它的调用过程却大不相同。

~ClassName() {//释放你的非托管资源}

比如类A中实现了Finalize函数,在A的一个对象a被创建时(准确的说应该是构造函数被调用之前),它的指针被插入到一个finalization链表中;在GC运行时,它将查找finalization链表中的对象指针,如果此时a已经是垃圾对象的话,它会被移入一个freachable队列中,最后GC会调用一个高优先级线程,这个线程专门负责遍历freachable队列并调用队列中所有对象的Finalize方法,至此,对象a中的非托管资源才得到了释放(当然前提是你正确实现了它的Finalize方法),而a所占用的内存资源则必需等到下一次GC才能得到释放,所以一个实现了Finalize方法的对象必需等两次GC才能被完全释放。

由于Finalize是由GC负责调用,所以可以说是一种自动的释放方式。但是这里面要注意两个问题:第一,由于无法确定GC何时会运作,因此可能很长的一段时间里对象的资源都没有得到释放,这对于一些关键资源而言是非常要命的。第二,由于负责调用Finalize的线程并不保证各个对象的Finalize的调用顺序,这可能会带来微妙的依赖性问题。如果你在对象a的Finalize中引用了对象b,而a和b两者都实现了Finalize,那么如果b的Finalize先被调用的话,随后在调用a的Finalize时就会出现问题,因为它引用了一个已经被释放的资源。因此,在Finalize方法中应该尽量避免引用其他实现了Finalize方法的对象。

可见,这“自动”释放资源的方法并不能满足我们的需要,因为我们不能显示的调用它(只能由GC调用),而且会产生依赖型问题。我们需要更准确的控制资源的释放

. Dispose

Dispose是提供给我们显示调用的方法。由于对Dispose的实现很容易出现问题,所以在一些书籍上(如《Effective C#》和《Applied Microsoft.Net Framework Programming》)给出了一个特定的实现模式:

class DisposePattern :IDisposable

{

private System.IO.FileStream fs = new System.IO.FileStream("test.txt", System.IO.FileMode.Create);

~DisposePattern()

{

Dispose(false);

}

IDisposable Members#region IDisposable Members

public void Dispose()

{

  //告诉GC不需要再调用Finalize方法,

  //因为资源已经被显示清理

    GC.SupdivssFinalize(this);

    Dispose(true);

}

#endregion

 

protected virtual void Dispose(bool disposing)

{

//由于Dispose方法可能被多线程调用,

//所以加锁以确保线程安全

lock (this)

{

  if (disposing)

  {

    //说明对象的Finalize方法并没有被执行,

    //在这里可以安全的引用其他实现了Finalize方法的对象

  }

  if (fs != null)

  {

    fs.Dispose();
  
    fs = null; //标识资源已经清理,避免多次释放

  }

  }

  }

}

 

在注释中已经有了比较清楚的描述,另外还有一点需要说明:如果DisposePattern类是派生自基类B,而B是一个实现了Dispose的类,那么DisposePattern中只需要override基类B的带参的Dispose方法即可,而不需要重写无参的Dispose和Finalize方法,此时Dispose的实现为:

class DerivedClass : DisposePattern

{

  protected override void Dispose(bool disposing)

{

lock (this)

{

  try

  {

    //清理自己的非托管资源,

    //实现模式与DisposePattern相同

  }

finally

{

  base.Dispose(disposing);

}

}

}

}

当然,如果DerivedClass本身没有什么资源需要清理,那么就不需要重写Dispose方法了,正如我们平时做的一些对话框,虽然都是继承于System.Windows.Forms.Form,
但我们常常不需要去重写基类Form的Dispose方法,因为本身没有什么非托管的咚咚需要释放。





 dispose实例

https://www.cnblogs.com/takako_mu/archive/2012/03/16/2399787.html

 

 

posted @ 2020-04-17 11:13  钢与铁  阅读(165)  评论(0编辑  收藏  举报