一、托管非托管
1、托管代码、非托管代码
托管代码 (managed code)
托管代码是由公共语言运行库环境(而不是直接由操作系统)执行的代码。托管代码应用程序可以获得公共语言运行库服务,例如自动垃圾回收、运行库类型检查和安全支持等。这些服务帮助提供独立于平台和语言的、统一的托管代码应用程序行为
非托管代码 (unmanaged code)
在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务;它与托管代码不同,后者从公共语言运行库中获得这些服务。
托管代码就是基于.net元数据格式的代码,运行于.net平台之上,所有的与操作系统的交换有.net来完成,就像是把这些功能委托给.net,所以称之为托管代码。非托管代码则反之。
2、托管资源和非托管资源
托管资源
托管资源是由公共语言运行的垃圾回收器进行分配和释放的数据(如int,string,float,DateTime等等,.net中超过80%的资源都是托管资源)。默认情况下, C#、Visual Basic 和 JScript.NET 数据是托管资源。不过,通过使用特殊的关键字,C# 数据可以被标记为非托管资源。Visual C++数据在默认情况下是非托管数据,即使在使用 /CLR 开关时也不是托管的。
非托管资源
非托管资源是CLR不能控制或者管理的部分。 最常见的一类非托管资源就是包装操作系统资源的对象(如文件,窗口或网络连接),对于非托管资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源,所以需要手动释放。
常见的非托管资源:ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,Timer,Tooltip 等资源
二、资源的释放
1、托管资源的释放:
.Net类型分为两大类,一个就是值类型,另一个就是引用类型。对于值类型是分配在栈上的,因此并不需要GC回收,而引用类型是分配在堆上的,需要GC来回收。
.NET中托管资源虽然由GC自动回收并不需要手动释放,但前提是要告诉GC该托管资源已经是垃圾。如果一个引用类型的对象或者其所包还的子对象没有任何引用是有效的,就认为该对象是垃圾(也就是可以被GC自动回收)。
托管资源也回存在内存泄露的问题,如果对象间存在交叉引用,则该对象无法被GC所回收。所以在确定一个对象不再使用(需要释放时)需要确定该对象没有被其他对象所引用。
2、非托管资源的释放
.Net提供如下几种常用方法:
- 1、使用using 语句或try/finally 块释放封装资源(常用)
- 2.析构函数(并不推荐)
- 3.实现Dispose方法(推荐)
- 4. 提供Close方法。
(1)使用using 语句或try/finally 块
1.1 Using 语句
使用using 语句获得一个或多个资源,执行指定的代码,在代码超出using语句的作用范围时,using语句会自动隐式调用该对象的Dispose()方法来释放对象(如代码A)。
使用using程序块的两个限制
- 为 using 语句提供的对象必须实现 IDisposable 接口,此接口提供了 Dispose 方法,该方法将释放此对象的资源,否则系统会提示编译错误(因为它是通过对象的Dispose方法实现的资源释放,如果不提供该方法怎么让它为你释放呢?)。
- using对象检查是静态类型检查,并不支持运行时类型检查,因此(如代码C)也会出现编译错误。
可以有多个对象与 using 语句一起使用,但是必须在 using 语句内部声明这些对象 。
A:
1 | Font font2 = new Font("Arial", 10.0f); |
2 | using (font2) |
3 | { |
4 | // use font2 |
5 | } |
6 |
B:
1 | using (Font font3 = new Font("Arial", 10.0f), |
2 | font4 = new Font("Arial", 10.0f)) |
3 | { |
4 | // Use font3 and font4. |
5 | } |
6 |
C:
1 | Font font2 = new Font("Arial", 10.0f); |
2 | Object font5=font2 ; |
3 | using (font5) |
4 | { |
5 | // use font2 |
6 | } |
7 |
1.2 try/finally 块
1 | Font font2; |
2 | try |
3 | { |
4 | font2 = new Font("Arial", 10.0f); |
5 | // use font2 |
6 | } |
7 | finally |
8 | { |
9 | if(font2!=null) |
10 | font2.Dispose(); |
11 | } |
12 |
2、析构函数
c#的析构函数有一下限制 :
- 不能在结构中定义析构函数。只能对类使用析构函数。
- 一个类只能有一个析构函数。
- 无法继承或重载析构函数。
- 无法调用析构函数。它们是被自动调用的。
- 析构函数既没有修饰符,也没有参数。
下面(代码A)是类 Car 的析构函数的声明,该析构函数代码被隐式地转换为(代码B):
A:
1 | class Car |
2 | { |
3 | ~ Car() // destructor |
4 | { |
5 | // cleanup statements... |
6 | } |
7 | } |
8 |
B:
1 | protected override void Finalize() |
2 | { |
3 | try |
4 | { |
5 | // cleanup statements... |
6 | } |
7 | finally |
8 | { |
9 | base.Finalize(); |
10 | } |
11 | } |
12 |
注意点:
- 子类的析构函数将对继承链中的所有实例递归地调用 Finalize 方法(从派生程度最大的到派生程度最小的)。
- 当对象符合析构时,垃圾回收器将运行对象的 Finalize 方法。
- 不应使用空析构函数。如果类包含析构函数,Finalize 队列中则会创建一个项。调用析构函数时,将调用垃圾回收器来处理该队列。如果析构函数为空,则只会导致不必要的性能丢失。
- 程序员无法控制何时调用析构函数,因为这是由垃圾回收器决定的。垃圾回收器检查是否存在应用程序不再使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存。程序退出时也会调用析构函数。
- 建议您提供一种在垃圾回收器释放对象前显式地释放资源的方式。可通过实现来自 IDisposable 接口的 Dispose 方法来完成这一点,该方法为对象执行必要的清理。这样可大大提高应用程序的性能。即使有这种对资源的显式控制,析构函数也是一种保护措施,可用来在对 Dispose 方法的调用失败时清理资源。
3、 实现Dispose方法
MSDN建议按照下面的模式实现IDisposable接口:
1 | public class Foo: IDisposable |
2 | { |
3 | public void Dispose() |
4 | { |
5 | Dispose(true); |
6 | GC.SuppressFinalize(this); |
7 | } |
8 | |
9 | protected virtual void Dispose(bool disposing) |
10 | { |
11 | if (!m_disposed) |
12 | { |
13 | if (disposing) |
14 | { |
15 | // Release managed resources |
16 | } |
17 | |
18 | // Release unmanaged resources |
19 | |
20 | m_disposed = true; |
21 | } |
22 | } |
23 | |
24 | ~Foo() |
25 | { |
26 | Dispose(false); |
27 | } |
28 | |
29 | private bool m_disposed; |
30 | } |
31 |
4、close方法
有时特定于域的名称比 Dispose 更合适。例如,文件封装可能需要使用 Close 方法名称。在此情况下,可以通过专用方式实现 Dispose 并创建调用 Dispose 的公共Close 方法。下面的代码示例阐释了这种模式。可以用适合您的域的方法名称替换 Close。此示例需要 System 命名空间。
1 | // Do not make this method virtual. |
2 | // A derived class should not be allowed |
3 | // to override this method. |
4 | public void Close() |
5 | { |
6 | // Call the Dispose method with no parameters. |
7 | Dispose(); |
8 | } |
9 |