转/ 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()
}
public
void
Dispose()
{
//这里释放一个句柄(句柄是一个非托管资源,属于Win32编程的概念)
Win32.DestroyHandle(
this
.CursorFileBitmapIconServiceHandle);
}
别忘了类中的托管资源还占着空间!
托管资源占着空间?你首先想到的可能是那些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; } }