.NET垃圾回收一

      在.NET中,系统资源是分为两种的,一种是托管资源,一个是非托管资源。对于托管资源,.NET的GC回收器能在某些特定的时刻(如内存不足的时候)对其进行回收。而非托管资源是必须进行显示释放的,GC回收器并不能对其进行回收。

      在.NET下,对一个类声明虚构函数的时候,C#编译器会将其编译为对Finalizer方法的调用。而有实现Finalizer方法和没有实现Finalizer方法在GC的回收过程中是有区别的。没有显示Finalizer的情况下:GC在回收的过程中,会去扫描一张表,该表记录着有实现Finalizer的类,若发现该类的实列没有再被引用并且在表中找不到记录,则直接对其进行回收。如果类实现Finalizer,则在给该类创建对象时,会现在表中记录该类分配的实列的地址,回收过程中,在表中找到记录,接着将这个记录再移到另外一张表中。在回收完成后,由另外一个线程去回收第二次记录的表的实列所占用的内存。因此可得知,实现了Finalizer的类,在回收的过程中,反而更消耗性能。

      那为什么还要定义一个Finalizer方法呢?这里就引出了非托管资源,非托管资源在GC回收过程中,是不会被自动回收的,我们需要显示释放,在.NET下的非托管资源的类都有实现一个Dispose方法。该方法用来实现对非托管资源的释放,如FileStream的Dispose方法。.NET提供了一个IDispose接口,用来实现定义对非托管资源实现释放的契约。其主要用法是在当某个类中包含有非托管资源的时候,对非托管资源进行释放(代码如下)。当我们不使用DisposeClass时,调用Dispose方法可以实现对其类中的非托管资源进行释放。

public class DisposeClass:IDisposable
{
public FileStream MyFileStream { get; set; }
public void Dispose()
{
this.MyFileStream.Dispose();
Console.WriteLine(
"释放资源");
}
}

      但是问题来了,如果我们忘记调用Dispose方法呢?那么上面的编码方式将使用非托管资源得不到释放。这时就需要利用到Finalizer函数了。也就是著名的IDispose模式。

MSDN上的代码:

      

View Code
1 public class MyResource : IDisposable
2 {
3 //非托管资源
4 private IntPtr handle;
5 //托管资源
6 private Component component = new Component();
7 // 初始状态设置为为释放
8 private bool disposed = false;
9
10 public MyResource(IntPtr handle)
11 {
12 this.handle = handle;
13 }
14
15 /// <summary>
16 /// 实现IDisposable接口,该接口的职责主要是释放托管资源
17 /// </summary>
18 public void Dispose()
19 {
20 Dispose(true);
21 //告诉GC在回收时不用调用虚构函数
22 GC.SuppressFinalize(this);
23 }
24
25 /// <summary>
26 ///
27 /// </summary>
28 /// <param name="disposing"></param>
29 private void Dispose(bool disposing)
30 {
31
32 if (!this.disposed)
33 {
34 //清理托管资源
35 if (disposing)
36 {
37 component.Dispose();
38 }
39
40 //清理非托管资源
41 CloseHandle(handle);
42 handle = IntPtr.Zero;
43 }
44 disposed = true;
45 }
46
47 [System.Runtime.InteropServices.DllImport("Kernel32")]
48 private extern static Boolean CloseHandle(IntPtr handle);
49
50 /// <summary>
51 /// 虚构函数清楚托管和非托管资源
52 /// </summary>
53 ~MyResource()
54 {
//这里是false的原因是:在调用Finalizer后,会自动帮我们清理托管资源,无需我们进行自动释放。
55 Dispose(false);
56 }
57 }

     可是自己疑问如果类中没有托管资源呢?那么还需要再定义一个protected类型的Dispose(bool)函数吗?可不可以直接这样:

public class DisposeClass:IDisposable
{
public FileStream MyFileStream { get; set; }
public void Dispose()
{
this.MyFileStream.Dispose();
GC.SuppressFinalize(
this);
}

~DisposeClass()
{
this.Dispose();
}
}

    如果是没有调用Dispose方法,而是在回收时调用GC.SuppressFinalize(this)这就显的多余。

    添加备注:在.NET下实现Dispose编程模式的类有很多。有些类还增加了Close,比如SqlConnection和FileStream。但是SqlConnectibion和FileStream的Close方法是有区别的。通过反编译查看代码可以知道,SqlConnection的Close是关闭了一个链接,如果连接池启用的话这个链接会放回连接池,如果连接池没有启用的话,这个链接会等到调用虚构函数的时候再被释放掉。但是对于FileStream的Close方法,其内部也会调用Dispose方法进行资源的释放。

posted @ 2011-03-09 12:25  雁北飞  阅读(186)  评论(0编辑  收藏  举报