The NOTE of learning ASP.NET [19] 关于GC(内存回收机制)、对象的销毁和using的使用

来源:互联网 发布:c语言实现多线程同步 编辑:程序博客网 时间:2024/05/15 23:43
    这两天看了用来回收无用对象并释放其占用的内存空间的GC(内存回收机制)的相关内容。。。一个教学视频整整看了5遍才理解他说的意思,都是英文太难为我了。看后知道了什么样的对象是在GC的回收范围内的,并且了解了GC只有在内存不够用时才进行内存的回收。接下来就应该瞧瞧对象销毁和GC之间运作的流程与关系了。
     当GC回收对象时会调用对象中的一个Finalize方法,虽然我们可能在我们的类中没有写这个方法,但是它确实存在。因为我们的所有类都是继承自System.Object类的,而Finalize方法是Object类的一个protected的方法。我们从来不会手动去调用那个方法,因为那个方法是当GC销毁对象时才会由GC自动调用的。而且大多数时候我们并不用去关心这个方法GC会适时的自动处理对象并释放内存等资源。那什么时候才需要关注这个Finalize方法呢?答案是:当我们调用了不是由.NET运行时环境管理的资源时,像打开了系统中的文件,数据流等,最典型的是我们的对象在运行时打开了一个数据库连接,这个数据库连接资源不会因为我们的对象被销毁而自动释放,所以我们需要在Finalize方法中设置相应的释放数据库连接的代码,这样即使我们在编码时没有手动关闭数据库连接,GC也会在回收对象时为我们释放数据库连接资源。可是System.Object的Finalize方法是被保护的不能够进行重载,如果我们需要自定义对象被销毁前要做的工作就需要使用.NET为我们提供的析构函数,析构函数与类名相同,在类名前加上~来标识。例如 Person类的析构函数就应该为 ~Person(){ //这里执行我们释放资源等处理的代码 }
    说到这可能会发现就这个例子来说如果我们在对象中打开了数据库连接而且没有关闭,即使我们提供了释放数据库资源连接的析构函数,那个数据库连接也不会马上关闭,因为GC只有当内存不足时才去回收那些无用对象并执行它的析构函数。那么对于这种使用过后需要立即释放的资源我们则需要使我们的类实现IDisposable接口,并实现接口中的Dispose方法。我们把释放数据库连接资源或其他不是由.NET管理的资源的代码放到Dispose方法中。似乎Dispose方法和析构函数做的工作是一样的,确实,但是他们两个还是有区别的,析构函数由GC调用,而Dispose由我们自己调用。这样我们就可以在使用完这个对象后立即执行其Dispose方法来释放其中的非.NET管理的资源(例如数据库连接)。
 
        下面是一个实现了IDisposable接口及Dispose方法的类:
using System;
using System.Collections.Generic;
using System.Text;
namespace ObjectsAndClasses
{
  class DisposeDemo : IDisposable
  {
    private System.IO.FileStream fs = null;
    //alreadyDisposed变量用来监视我们的对象是否已经执行过Dispose方法
    private bool alreadyDisposed = false;

    public void Dispose()
    {
      if (!this.alreadyDisposed)
      {
        if (fs != null)
          fs.Close();
        Console.WriteLine("In Dispose");
      }
      this.alreadyDisposed = true;
      // 下面这行代码告诉GC在销毁对象的时候不用
      // 执行这个对象的析构函数,因为我们已经在Dispose
      // 方法中做了析构函数需要做的工作。这样可以使GC
      // 在回收对象时提高一些效率
     GC.SuppressFinalize(this);
    }
  }
}
 
下面是调用上面所写的类的示例:
    private static void TestDispose()
    {
      // Declare the variable:
      DisposeDemo demo = null;
      try
      {
        // Assign the variable a value:
        demo = new DisposeDemo();
      }
      finally
      {
        if (demo != null)
        {
          demo.Dispose();
        }
      }
    }
 
上面的方法可能使用起来稍微麻烦,其实我们还可以使用using,代码如下:
    private static void TestUsing()
    {
      // 下面代码同样是创建了一个对象赋值给了demo这个引用变量,
      // 只不过它被包含在using后边的括号中,使用了using 后,demo
      // 这个对象的域就限制在了using后的大括号中。当大括号中的代码
      // 执行到了末尾后则会自动调用demo对象的Dispose方法来对资源
      // 进行释放。实际上这段代码的作用和上一段是一样的,只是使用起
      // 来更为简便。需要注意的是,只有实现了IDisposable接口的类才能
      // 使用using来操作。
      using (DisposeDemo demo = new DisposeDemo())
      {
        // Do something with demo in here...
      }
    }
原创粉丝点击