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方法必定被調用。

相關文章
相關標籤/搜索