.net 资源释放(托管资源和非托管资源)

1、托管资源

像int、float、DateTime等都是托管资源;net中80%的资源都是托管资源;

托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我们无法控制系统在什么时间回收资源。

2、非托管资源

像ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,OdbcDataReader,OleDBDataReader,Pen,

Regex,Socket,StreamWriter,Timer,Tooltip 等资源,都是非托管资源。

垃圾回收器对非托管资源一无所知,无法自动回收非托管资源;我们必须显示的释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。

——————————————————————————————————分割线———————————————————————————————————————————————

一、非托管资源如何释放,.NET Framework 提供 Object.Finalize 方法,它允许对象在垃圾回收器回收该对象使用的内存时适当清理其非托管资源。

非托管资源通过Object.Finalize方法回收,如果您要让垃圾回收器在回收对象的内存之前对对象执行清理操作,您必须在类中重写 Finalize 方法。然而大家都可以发现在实际的编程中根本无法override方法Finalize(),在C#中,可以通过析构函数自动生成 Finalize 方法和对基类的Finalize 方法的调用。

protected override void Finalize()
{
     Console.WritleLine("析构函数执行...");
}

当我们当我们编译这段代码的时候,我们发现编译器会报如下的错误:

这是因为编译器彻底屏蔽了父类的Finalize方法,编译器提示我们如果要重写Finalize方法我们要提供一个析构函数来
代替,下面我们就提供一个析构函数:

~CaryClass()
  {
      Console.WriteLine("析构函数执行...");
  }

实际上这个析构函数编译器会将其转变为如下代码:

protected override void Finalize()
{
   try
   {
     Console.WritleLine("析构函数执行...");
   }
   finally
   {
     base.Finalize();
   }
}

在编程中,并不建议进行override方法Finalize(),因为,实现 Finalize 方法或析构函数对性能可能会有负面影响。一个简单的理由如下:用 Finalize 方法回收对象使用的内存需要至少两次垃圾回收,当垃圾回收器回收时,它只回收没有终结器(Finalize方法)的不可访问的内存,这时他不能回收具有终结器(Finalize方法)的不可以访问的内存

二、一个推荐替代析构函数的方式是使用System.IDisposable接口

首先来看MSDN中关于这个接口的说明:

[ComVisible(true)]
public interface IDisposable
{
    // Methods
    void Dispose();
}

1.[ComVisible(true)]:指示该托管类型对 COM 是可见的.

2、此接口的主要用途是释放非托管资源。当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。但无法预测进行垃圾回收的时间。另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。将此接口的Dispose方法与垃圾回收器一起使用来显式释放非托管资源。当不再需要对象时,对象的使用者可以调用此方法。

基本应用:

public class CaryClass :IDisposable
 {
    public void DoSomething()
    {
       Console.WriteLine("Do some thing....");
    }
    public void Dispose()
    {
       Console.WriteLine("及时释放资源");
    }
 }

我们用两种方式调用

1.1.第一种方式,使用Using语句会自动调用Dispose方法,代码如下:

using (CaryClass caryClass = new CaryClass())
 {
     caryClass.DoSomething(); 
 }

1.2第二种方式,现实调用该接口的Dispose方法,代码如下:

CaryClass caryClass = new CaryClass();
try 
{ 
    caryClass.DoSomething(); 
}
finally 
{ 
    IDisposable disposable = caryClass as IDisposable; 
    if (disposable != null) 
        disposable.Dispose(); 
}    

两种方式的执行结果是一样的:

 3、经典应用 Disposable 模式

private bool IsDisposed=false;  
 public void Dispose()  
 {  
     Dispose(true);  
     GC.SupressFinalize(this);  
 }  
 protected void Dispose(bool Diposing)  
 {  
     if(!IsDisposed)  
     {  
         if(Disposing)  
         {  
            //清理托管资源
         }  
         //清理非托管资源
     }  
     IsDisposed=true;  
 }  
 ~CaryClass()  
 {  
     Dispose(false);  
 } 

在这个模式中,void Dispose(bool disposing)函数通过一个disposing参数来区别当前是被Dispose()调用还是被析构函数调用(当disposing为“true”时,说明Dispose()是被程序显示调用的,需要释放托管资源和非托管资源;当disposing为“false”时,说明Dispose()是被析构函数(也就是C#的Finalize())调用的,只需要释放非托管资源)。

这是因为,Dispose()函数是被其它代码在程序中显式调用并要求释放资源的,而Finalize是被GC调用的。在GC调用的时候MyResource所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize。

然而,即使重复调用Finalize和Dispose也是不存在问题的,因为有变量m_disposed的存在,资源只会被释放一次,多余的调用会被忽略过去。

因此,上面的模式保证了:
1、 Finalize只释放非托管资源;
2、 Dispose释放托管和非托管资源;
3、 重复调用Finalize和Dispose是没有问题的;
4、 Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。

原文地址:https://www.cnblogs.com/panbin/p/5337798.html