转/ C# 托管资源和非托管资源

原文

对于这两个一直就是模模糊糊的,半知零解

托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源托管资源的回收工作是不需要人工干预的,由.NET运行库在合适时调用垃圾回收器进行回收

非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等。

这类资源,垃圾回收器在清理的时候会调用Object.Finalize()(完成的意思)方法。默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源。

(就是.Net可以自己进行回收的资源是托管资源,自己不知道回收回收的就是非托管。)

注:

什么是托管资源和非托管资源?

托管资源就是托管给CLR的资源,CLR能对这些资源进行管理。

而非托管资源则是CLR无法对这些资源管理,这些资源的申请、释放必须由使用者自行管理。

例如,像Win32编程中的文件句柄,上下文句柄、窗口或网络连接等资源都属于非托管资源。但是如果这些非托管资源在.Net中进行了封装,成为了.Net类库中的一部分,它就不属于非托管资源了,因为在对它们封装的过程中,就实现了它们的自动管理功能。

也就是说,你能在.Net中找到的类产生的对象,都是托管资源。

 

 

如果按照上面做法,非托管资源也能够由垃圾回收器进行回收,但是非托管资源一般是有限的,比较宝贵的,而垃圾回收器是由CRL自动调用的,这样就无法保证及时的释放掉非托管资源,因此定义了一个Dispose()方法,让使用者能够手动的释放非托管资源。Dispose()方法释放类的托管资源和非托管资源,使用者手动调用此方法后,垃圾回收器不会对此类实例再次进行回收。Dispose()方法是由使用者调用的,在调用时,类的托管资源和非托管资源肯定都未被回收,所以可以同时回收两种资源。 Microsoft为非托管资源的回收专门定义了一个接口:IDisposable,接口中只包含一个Dispose()方法。任何包含非托管资源的类,都应该继承此接口。

原文

首先,对于垃圾回收而言,在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作;而对于一些非托管资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收。那么什么时候使用Idisposable接口,以及如何使用呢?

public interface IDisposable
{
  void Dispose();
}
public class DisposablClass : IDisposable
{
  //是否回收完毕
  bool _disposed;
  public void Dispose()//重写上面接口中的方法
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }
  ~DisposableClass()
  {
    Dispose(false);
  }   
  //这里的参数表示示是否需要释放那些实现IDisposable接口的托管对象
  protected virtual void Dispose(bool disposing)
  {
    if(_disposed) return; //如果已经被回收,就中断执行
    if(disposing)
    {
      //TODO:释放那些实现IDisposable接口的托管对象
    }
    //TODO:释放非托管资源,设置对象为null
    _disposed = true;
  }
}

  

Dispose()方法

需要回收非托管资源的DisposableClass类,就调用Dispoase()方法。而这个方法不会被CLR自动调用,需要手动调用

总结:当我们自定义的类及其业务逻辑中引用某些托管和非托管资源,就需要实现IDisposable接口,实现对这些资源对象的垃圾回收。

 

我们需要编写一种方法去释放!

为了去清除一些非托管资源,你创建的类需要有一个public方法,方法的名字可以随意命名

例如:

public void Cleanup()
public void Shutdown()
你可以这么做,但是有一个标准的名字

public void Dispose()

甚至有一个接口IDisposeable,里面包含的就是刚才那个方法:

public interface IDisposable
{
 void Dispose()
}
 
因此最好的办法是让你的类去实现IDisposable接口,在接口内的Dispose方法内提供一段清除非托管资源的代码
 
public void Dispose()
{
 //这里释放一个句柄(句柄是一个非托管资源,属于Win32编程的概念)
 Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
 
OK。这就完成了,除非你想做的更好!

别忘了类中的托管资源还占着空间!

托管资源占着空间?你首先想到的可能是那些int,string等等这些托管资源,它们能占用几个空间,他们占着就占着呗!

但是托管资源可不仅仅是那些资源,要是你的对象使用了250MB的System.Drawing,Bitmap(这是在.Net Frame中的,属于托管资源)作为一些缓冲怎么办?当然,你知道这是一个.Net的托管资源,所以GC理所应当的将会释放它。但是你真的想留着250MB的内存空间就那么被占用着?然后等待着GC最终释放它?更或者要是有一个更大数据库连接呢?我们当然不想让那连接白白占用来等待GC的终结!

如果用户调用了Dispose方法(意味着他们不再想使用这个对象里的一切)

为什么不去扔掉那些浪费空间的位图资源和数据库连接呢?

那么,我们就应该这么做:

  • 释放非托管资源(因为我们必须这么做)
  • 释放托管资源(让你的Dispose更完美)

所以,让我们更新我们的Dispose方法来释放那些托管资源

public void Dispose() 
{ 
 //Free unmanaged resources 
 Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); 
  
 //Free managed resources too 
 if (this.databaseConnection !=null) 
 { 
 this.databaseConnection.Dispose(); 
 this.databaseConnection =null; 
 } 
 if (this.frameBufferImage !=null) 
 { 
 this.frameBufferImage.Dispose(); 
 this.frameBufferImage = null; 
 } 
}

  转自

 

 
 

 

posted @ 2018-08-29 15:11  ProZkb  阅读(442)  评论(0编辑  收藏  举报