程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#垃圾回收,

C#垃圾回收,

編輯:C#入門知識

C#垃圾回收,


         析構方法:     

       我們知道引用類型都有構造方法(constructor),相對應的也有一個析構方法(destructor).顧名思義,構造方法,就是在創建這個對象時,要執行的方法。例如,我們可以通過構造方法,

初始化字段。析構方法,就是當這個對象被垃圾回收後(garbage collected,我們稱回收對象內存為垃圾回收 garbage collection),要執行的方法。關於析構方法,需要大家注意的是,垃圾回收一個對象,並不是析構方法完成的(下面會講到垃圾回收的工作原理),析構方法只有在對象被垃圾回收後才執行。也就是說,析構方法對於一個對象來講,不是必須的。很多時候,如果加上它,反而不好(下面講garbage collector怎樣工作時,就會明白不好的原因)。 
     

          既然垃圾回收不歸析構方法負責,那麼它有什麼用呢?因為垃圾回收是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().

這裡有幾條對析構方法的限制:

1.只有引用類型才可以有析構方法。

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved