.NET托管资源与非托管资源介绍
在.NET中,每一种类型都代表一种资源,而资源分为托管资源与非托管资源两类:
1) 托管资源:由CLR分配和释放的资源,即从CLR中new声明的对象,一般指托管内存。托管资源进一步又可分为:
a、普通类型托管资源:如int、String、float等;
b、非普通类型托管资源:继承了IDispose接口的类实例化后对应的对象,此类资源被GC回收时需经2个步骤,会降低系统性能,因此在实现Dispose方法时需手动清理;
2) 非托管资源:不受CLR管理的对象,如Windows内核对象、文件、数据库连接、套接字、COM对象等;
注:1) 继承IDispose接口的类实例化的对象不是非托管资源,个人理解:非托管资源几乎无法在程序中自定义。
2) 个人理解,数据库连接如SqlConnection本身是托管资源,只是内部包含了非托管资源-数据库连接的系统资源。
——————————————————–
如果我们定义的类型使用到了非托管资源或非普通类型托管资源,就需要显示地释放托管资源,实现方法是让类型继承IDispose接口,并实现接口中的Dispose方法。标准的Dispose模式实现方式如下:
![](https://tiantai.live/wp-content/uploads/2022/02/366a5bfb4df8358bf69d7e8f8829fa4c_0.3898004256029479.png)
//演示需要,自定义非普通托管类型 class MyResource : IDisposable { public void Dispose(){ } } public class SampleClass : IDisposable { //演示创建一个非托管资源 private IntPtr nativeResource = Marshal.AllocHGlobal(100); //演示创建一个非普通类型托管资源,MyResource实现了接口IDisposable,是一个非普通类型的托管资源 private MyResource myResource = new MyResource(); //演示创建一个普通类型的托管资源 private String name = "hduhans"; private bool disposed = false; /// <summary> /// 实现IDisposable中的Dispose方法 /// </summary> public void Dispose() { //必须为true,显示释放所有资源 Dispose(true); //通知垃圾回收机制不再调用终结器(析构器),也就是GC不会调用~SampleClass()方法 GC.SuppressFinalize(this); } /// <summary> /// 必须,防止程序员忘记了显式调用Dispose方法,这时可由GC来回收并释放非托管资源 /// 注意在析构函数中不允许释放托管资源,因为GC在调用析构函数前可能已经释放了托管资源,此处再释放可能会引发异常 /// </summary> ~SampleClass() { //必须为false Dispose(false); } /// <summary> /// 如果子类继承当前类型并实现了自己的Dispose方法,必须调用父类的Dispose方法来释放父类中的资源,调用:base.Dispose(disposing) /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { if (disposed) { return; } if (disposing) { // 清理非普通类型(继承IDispose接口)的托管资源,普通类型的托管资源(此例中name)不需要手动清理,GC会自动清理 if (myResource != null) { myResource.Dispose(); myResource = null; } } // 清理非托管资源 if (nativeResource != IntPtr.Zero) { Marshal.FreeHGlobal(nativeResource); nativeResource = IntPtr.Zero; } //让类型知道自己已经被释放 disposed = true; } }
此例中需要注意的是:
1) 即使提供了显示释放的方法Dispose,也应该在终结器中提供隐式清理操作,即在终结器(析构函数)中释放非托管资源。如果程序员忘了调用Dispose方法,则只能靠终结器来释放非托管资源,否则非托管资源无法释放,从而造成内存泄漏。此例:~SampleClass(){ Dispose(false); }
2) 在Dispose模式中应提供受保护的虚方法,如果子类继承当前类型并实现了自己的Dispose模式,受保护的虚方法用来提醒子类:必须在实现自己清理方法时注意到父类的清理工作,即子类需调用base.Dispose()。此例:protected virtual void Dispose(bool disposing){ //… }
——————————————————–
对于继承IDispose接口的对象,可用using语法糖:
using (SampleClass samleClass = new SampleClass()) { //操作... }
等效于:
SampleClass samleClass = null; try { samleClass = new SampleClass() //操作... } finally { samleClass.Dispose(); }