C#垃圾回收

         析构方法:     ide

       咱们知道引用类型都有构造方法(constructor),相对应的也有一个析构方法(destructor).顾名思义,构造方法,就是在建立这个对象时,要执行的方法。例如,咱们能够经过构造方法,this

初始化字段。析构方法,就是当这个对象被垃圾回收后(garbage collected,咱们称回收对象内存为垃圾回收 garbage collection),要执行的方法。关于析构方法,须要你们注意的是,垃圾回收一个对象,并非析构方法完成的(下面会讲到垃圾回收的工做原理),析构方法只有在对象被垃圾回收后才执行。也就是说,析构方法对于一个对象来说,不是必须的。不少时候,若是加上它,反而很差(下面讲garbage collector怎样工做时,就会明白很差的缘由)。 
      spa

          既然垃圾回收不归析构方法负责,那么它有什么用呢?由于垃圾回收是CLR自动执行的,CLR只能处理受管理资源(managed resource),那些不受管理资源(unmanaged resource)就须要咱们线程

本身去处理了。例如文件读取(file stream),当对象结束时,咱们须要把文件流关掉。关掉文件流的代码,就要在析构方法中。也就是说,析构方法的用处,在处理不受管理资源时用处比较大。
看个例子:设计

class FileProcessor
{
     FileStream file=null;
public FileProcess(string fileName)
{
     this.file=file.OpenRead(fileName); //open file for reading
}

~FileProcess()    //析构方法与构造方法很类似,不一样的是析构方法要加一个~
{
     this.file.Close(); //close file
}
}

file对象,是CRL垃圾回收,当file被垃圾回收后,运行析构方法FileProcess,此时咱们将非管理资源关掉。this.file.Close().code

这里有几条对析构方法的限制:对象

1.只有引用类型才能够有析构方法。blog

struct MyStruct(){
     ~MyStruct(){....}//结构是值类型,因此不能有析构方法 
      }

2.不能对析构方法提供访问修饰符.继承

public ~FileProcessor(){};//错误的

3.析构方法不能够有参数.接口

~FileProcessor(int param)
{
....// //错误的
};

之因此会有这三条限制,是由于析构方法只能由CLR调用,本身不能够调用。由于,你不知道引用对象何时被垃圾回收了,只有对象被垃圾回收了,程序才会本身调用析构方法。

在内部,程序会将咱们写的析构方法,转换一下。例如:

class FileProcessor
{
    ~fileProcessor(){...}   //析构方法
}

class FileProcessor
{

 protected override void Finalize()
{
      try{

           }
finally{
      base.Finalize();      //CLR会将析构方法转变成这个 
       }
 }
 }

咱们把执行析构方法的过程,称为结束(finalization,或终结。)

 

垃圾回收机制(garbage collector)

      咱们上边提到,回收内存空间,回收不用的引用类型对象的过程称为垃圾回收(garbage collection).这个过程是由CLR经过Garbage collector这样一个机制去运行的。

当咱们在程序中建立变量,会在内存中开辟一段空间。电脑的内存不是无限大的,咱们须要在变量超越定义的范围(程序再也不须要这个变量了)时,对它所占的内存进行管理,处理这些内存。当变量再也不被使用时,须要把内存回收。值类型变量回收内存,很是简单。

当它超出定义的范围时就会自动被毁掉,被占的内存也会自动回收。超出定义的范围,指的是当它再也不被使用,不能再被使用。引用类型变量回收内存,比较麻烦。例如:

fileProcessor myFp=new fileProcessor();
fileProcessor referenceToMyFp=myFp;


想一下这种状况,myFp对象已经超出定义的范围。此时咱们去回收内存,要把myFp引用堆上的内存回收。但是,偏偏此时,referenceToMyFp还在引用准备回收的内存,若是此时把内存回收,当程序运行referenceToMyFp时,程序就会出错。因此,只用当全部引用对象都超出定义的范围时,也就是都再也不使用时,才能够去回收这些对象引用的内存。确保程序中这些指向同一起的引用对象所有再也不使用,是很困难,很复杂的.因此C#设计者,把处理引用类型回收内存的工做,交给了CLR(Common language running).CLR利用garbage collector机制,来处理这些事情。

垃圾回收机制工做原理

      garbage collector在本身的线程中工做,在特定的时间执行。通常,当程序运行到一个方法的最后时,就会工做。它工做时,其余线程就会暂时中止工做。由于,garbage collector可能会移除或者更新对象引用。

1.garbage collector会建立一张表,表里存放全部的可获得对象(reachable objects,.可获得对象,说白了就是指那些还在使用,不能回收内存的对象。)。

2.检查一下那些不可获得对象(unreachable objects,就是那些超出定义范围,须要回收内存的对象),看看他们是否有析构方法。(destructor),若是有,就把这些对象放入一个叫作

freachable queue的队列里。

3.把那些不可获得对象,且没有析构方法的对象所指向的内存地址回收。它是经过将那些可得对象在堆上的地址下移,这样堆上面就留出了可用的内存。此时,garbage collector也会更新堆

上的引用地址。(由于,地址有变化)。

4.此时,程序中其余的线程恢复工做。

5.garbage collector 经过调用本身的Finalize方法来结束不可获得对象,且有析构方法的对象。(前面咱们讲了,析构方法不是必须的,有时候会给程序带来复杂,累赘。若是,没有析构方法,当

CLR运行Garbage collector时,第五步就能够省去)。

 

资源管理

       有些资源很稀缺,稀缺到不能等到CLR去调用析构方法去处理。例如database connections,file handles.此时,咱们就须要写一个dispose方法,手动去处理资源。(dispose能够换成任何名

字,这里只是举个例子)。例如:

TextReader reader=new StreamReader(filename);
string line;
while((line=reader.ReadLine())!=null)
{
     Console.WriteLine(line);
}
reader.Close();

这里,Close就是一个dispose方法,手动去关掉文件流。可是,这样有个问题,当出现异常时,有可能致使reader.Close()不被执行,这样资源一直被占用。因此,咱们要改写成:

TextReader reader=new StreamReader(filename);
try
{
    string line;
    while((line=reader.ReaderLine())!=null)
{
      Console.WriteLine(line);
}
}
finally
{
   reader.Close();
}

这样不管如何,reader.Close()都会被执行,资源都会被释放。不过即使改写成这样,也不是最完美的。由于,当咱们执行完finally块儿内的代码,也就是在try finally以后,咱们有可能

无心中使用reader对象,就是释放掉资源后的reader对象。此时,咱们能够用using 语句来解决这些不足。咱们将上面的代码改为using以后:

using(TextReader reader=new StreamReader(filename))
{
    string line;
    while((line=reader.ReadLine())!=null)
{
        Console.Writle(line);
}
}

执行完using以后,资源自动释放,越过using范围,对象不能用。一个对象若是要被支持使用using,该对象必须实现IDispose接口。

咱们本身建立一个类,继承IDispose接口,让它能够用在USING语句中。这里咱们应该区分一下析构方法,与dispose方法。咱们知道析构方法必定会执行,可是不知道何时执行。咱们

知道dispose方法何时执行,可是不知道它会不会执行,由于类里有dispose方法,不表明必定会把这个类用在using语句中。此时,咱们就能够经过析构方法来调用dispose方法,这样可

以保证dispose方法必定被调用。

相关文章
相关标签/搜索