Archive

Archive for 2010年6月

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

2010年06月1日 留下评论
    这两天看了用来回收无用对象并释放其占用的内存空间的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…
      }
    }
Advertisements
分类:ASP.NET

The NOTE of learning ASP.NET [18] 关于.NET(GC)内存回收机制

2010年06月1日 留下评论
    在.NET中不管是哪种类型的变量都占据着内存中的一段空间。int,datetime,float等基本数据类型在对应的内存空间中存放的是实际的数据,而引用变量则不同,引用变量所占的内存空间中存放的不是实际的对象数据,而是一段指向存放实际对象所在内存的地址,这个在C或者C++中被叫做指针,指向存放实际对象的内存的地址。一般创建一个引用变量并为它赋值的的流程(以创建Person类的对象为例):
    Person personOne = new Person();
    这条语句实际上分为三个步骤,
   (1)使用new命令在内存中创建了一个新的对象
   (2)创建Person类型的引用变量personOne(引用变量在使用前必须为其赋值,否则会编译报错,引用变量被声明后若未赋值它的值为null即未引用到任何对象)
   (3)将新建的对象所在内存的信息赋值给应用变量personOne。
    可以看出如果我们做了如上操作后再使用 personOne = null; 将personOne赋予null值的话,虽然引用变量被设置为空,但是它原先引用的对象实际上仍然存在于内存中。而我们其实并不用担心如何释放那个已经不再使用的对象所占的内存。.NET中的内存回收机制即GC(Garbage Collector)负责回收引用变量及其引用的实际对象的内存空间。
    我们需要关心两个问题:具备什么条件的对象会被GC回收并释放内存空间?GC什么时候才会回收那些已经无用的对象?
   (1)当一个内存中的对象不再被任何引用变量所引用时,它符合了被GC回收的条件。比如Person personOne = new Person();当我们把personOne设置成null时,personOne不再指向任何对象,而同时我们也无法再次访问之前personOne所指向的对象,所以这个对象对我们来说已经是没有用处的了。
   (2)GC不会在对象符合被回收的条件时立即进行回收并释放内存,它会判断在内存不够用时再去回收内存中的对象,如果内存足够大的话,它可能永远不会去回收那些仍存在于内存中但是已经无用的对象。
    了解了以上问题,就可以在编程中更好的控制资源的使用并更好的理解一些方法的用途。
   
分类:ASP.NET