C#资源释放

虽然在.NET编程过程当中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有不少须要咱们编码回收。掌握托管与非托管的基本知识,能够有效避免某些状况下致使的程序异常。html

1.托管与非托管

1.1什么是托管与非托管?

托管代码就是Visual Basic .NET和C#编译器编译出来的代码。编译器把代码编译成中间语言(IL),而不是能直接在你的电脑上运行的机器码。中间语言被封装在一个叫程序集(assembly)的文件中,程序集中包含了描述你所建立的类,方法和属性(例如安全需求)的全部元数据。你能够拷贝这个程序集到另外一台服务器上部署它。一般来讲,这个拷贝的动做就是部署流程中惟一的一个操做。数据库

托管代码在公共语言运行库(CLR)中运行。这个运行库给你的运行代码提供各类各样的服务,一般来讲,他会加载和验证程序集,以此来保证中间语言的正确性。当某些方法被调用的时候,运行库把具体的方法编译成适合本地计算机运行的机械码,而后会把编译好的机械码缓存起来,以备下次调用(这就是即时编译)。随着程序集的运行,运行库会持续地提供各类服务,例如安全,内存管理,线程管理等等。这个程序被“托管”在运行库中。Visual Basic .NET和C#只能产生托管代码。若是你用这类语言写程序,那么所产生的代码就是托管代码。编程

托管资源:通常是指被CLR(公共语言运行时)控制的内存资源,这些资源由CLR来管理。能够认为是.net 类库中的资源。c#

非托管资源:不受CLR控制和管理的资源。缓存

对于托管资源,GC负责垃圾回收。对于非托管资源,GC能够跟踪非托管资源的生存期,可是不知道如何释放它,这时候就要人工进行释放。安全

1.2哪些资源是非托管的?

非托管代码就是在Visual Studio .NET 2002发布以前所建立的代码,例如Visual Basic 6, Visual C++ 6。 最糟糕的是,连那些依然残存在你的硬盘中、拥有超过15年历史的陈旧C编译器所产生的代码都是非托管代码。非托管代码直接编译成目标计算机的机械码,这些代码只能运行在编译出它们的计算机上,或者是其它相同处理器或者几乎同样处理器的计算机上。非托管代码不能享受一些运行库所提供的服务,例如安全和内存管理等。若是非托管代码须要进行内存管理等服务,就必须显式地调用操做系统的接口,一般来讲,它们会调用Windows SDK所提供的API来实现。就最近的状况来看,非托管程序会经过COM接口来获取操做系统服务。跟Visual Studio平台的其余编程语言不同,Visual C++能够建立非托管程序。当你建立一个项目,而且选择名字以MFC,ATL或者Win32开头的项目类型,那么这个项目所产生的就是非托管程序。服务器

总而言之,非托管代码是运行在公共语言运行库环境(CLR)的外部,由操做系统直接执行的代码。非托管代码必须提供本身的垃圾回收、类型检查、安全支持等服务;它与托管代码不一样,后者从公共语言运行库中得到这些服务。网络

整体来讲就是 不受CLR控制和管理的资源编程语言

包括:好比文件流、图像图形类、数据库的链接,网络链接,系统的窗口句柄,打印机资源等,这类资源通常不存在堆上。能够认为操做系统资源的一组API。函数

原则:若是咱们的类使用的非托管资源,如数据库链接、文件句柄,这些资源需作到:使用后马上释放。

1.3托管代码的执行过程

  1. 选择编译器:为得到公共语言运行库提供的优势,必须使用一个或多个针对运行库的语言编译器,如 Visual Basic、C#、Visual C++、JScript 或许多第三方编译器(如 Eiffel、Perl 或 COBOL 编译器)中的某一个。因为运行库是一个多语言执行环境,所以它支持各类数据类型和语言功能。您所用的语言编译器首先肯定可用的运行库功能,而后使用这些功能设计代码。编译器(而不是运行库)创建代码必须使用的语法。若是您的组件必须彻底可以被用其余语言编写的组件使用,您的组件的导出类型必须只公开公共语言规范 (CLS) 中包括的语言功能。
  2. 编译,将源代码翻译为microsoft中间语言(MSIL)并生成所需的元数据。
  3. 在执行时,实时 (JIT) 编译器将 MSIL 翻译为本机代码。在此编译过程当中,代码必须经过验证过程,该过程检查 MSIL 和元数据以查看是否能够将代码肯定为类型安全。
  4. 运行代码:公共语言运行库提供使执行可以发生以及可在执行期间使用的各类服务的结构。

2.如何释放非托管资源?

.NET对于释放资源,标准作法以下:

(1)继承IDisposable接口;

(2)实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象自己从垃圾回收器中移除(垃圾回收器不在回收此资源);

(3) 实现类析构函数,在其中释放非托管资源。

 对于Dispose()方法的几个参数说明:

A. 参数为true表示释放全部资源,只能由使用者调用

  参数为false表示释放非托管资源,只能由垃圾回收器自动调用

C. 若是子类有本身的非托管资源,能够重载这个函数,添加本身的非托管资源的释放

D.可是要记住,重载此函数必须保证调用基类的版本,以保证基类的资源正常释放 

2.1Dispose的实现方法

...有时间再写..

2.2举例说明资源释放

2.2.1 数据库链接释放

(1)错误作法:  

SqlConnection conn = new SqlConnection();
//do something;
conn.Dispose();

此段代码,若是出现异常conn.Dispose()未能及时执行,会致使没有及时关闭链接。

(2)正确作法: 

SqlConnection conn = new SqlConnection();
try
{
    //do something;
}
finally
{
    conn.Dispose();
}

对于以上代码,咱们还能够简化代码量,c#使用using简化输入,编译器自动翻译成 try...finally,改成下面写法

using(SqlConnection conn = new SqlConnection())
{
      //do something;
}

2.3对于释放资源注意的几点

A. 显示调用Dispose()方法,能够及时的释放资源,同时经过移除Finalize()方法的执行,提升了性能;

B. 若是没有显式调用Dispose()方法,垃圾回收器也能够经过析构函数来释放非托管资源,垃圾回收器自己就具备回收托管资源的功能,从而保证资源的正常释放,只不过由垃圾回收器回收会致使非托管资源的未及时释放的浪费。

C. 在.NET中应该尽量的少用析构函数释放资源。在没有析构函数的对象在垃圾处理器一次处理中从内存删除,但有析构函数的对象,须要两次,第一次调用析构函数,第二次删除对象。并且在析构函数中包含大量的释放资源代码,会下降垃圾回收器的工做效率,影响性能。

D. 对于包含非托管资源的对象,最好及时的调用Dispose()方法来回收资源,而不是依赖垃圾回收器

E. 析构函数只能由垃圾回收器调用。

F. Despose()方法只能由类的使用者调用。

G. 在.NET中,凡是继承了IDisposable接口的类,均可以使用using语句,从而在超出做用域后,让系统自动调用Dispose()方法。
H. 一个资源安全的类,都实现了IDisposable接口和析构函数。提供手动释放资源和系统自动释放资源的双保险。

2.4 关于Finalize和Dispose

(1)、Finalize只释放非托管资源;

(2)、Dispose释放托管和非托管资源;

(3)、重复调用Finalize和Dispose是没有问题的;

(4)、Finalize和Dispose共享相同的资源释放策略,所以他们之间也是没有冲突的。

 

==========================【Origin and Reference】==========================

https://www.cnblogs.com/yubinfeng/p/4625833.html

https://www.cnblogs.com/yangecnu/archive/2013/04/30/3052652.html

https://www.2cto.com/kf/201007/52838.html