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