1. 程式人生 > >C#垃圾回收和解構函式以及弱引用

C#垃圾回收和解構函式以及弱引用

在程式中,當我們每建立一個物件,就會在記憶體中開闢一個空間,用以存放這個物件。如果建立的物件多了,記憶體就會出現不夠用的情況。這時我們就要把記憶體中不再使用的物件釋放掉,避免記憶體的佔用及程式的異常。這個過程就是垃圾回收。手動進行垃圾回收的方法是:GC.Collect(); ,但我們一般不會這麼做,一來是因為我們宣告的物件有可能還會被引用,而且在手動回收的時候會漏掉很多,二來VS為每個程式提供自動回收垃圾的功能,不需要我們手動進行。

CLR的一個核心功能就是進行垃圾回收。垃圾回收的目的就是提高記憶體利用率。

垃圾回收器,只回收託管堆中的記憶體資源,不回收其它資源(資料庫連線、檔案控制代碼、網路埠等)。

那什麼樣的物件才會被回收呢?在記憶體中,只要沒有變數引用的物件,表示這個物件可以被回收了(null)。但是執行垃圾回收的時間是不確定的,一般是當程式需要新記憶體的時候開始執行回收。另外我們還可以手動呼叫垃圾回收器:GC.Collect(); ,但不建議使用,因為程式流程在垃圾回收的時候會短暫的暫停一下。

在C#中,垃圾回收器是用“代”的概念來區分的。一共有3代:第0代,第一代,第二代。各代的回收機率是第0代最高,第一代次之,最後是第2代。它的執行過程是當第0代進行垃圾回收之後,剩餘沒有被釋放的物件就會轉到第一代裡面去。如果第1代的記憶體已滿,再來把第1代中的物件進行垃圾回收,把剩餘沒被釋放的物件存入第2代,這樣第1代就會有空間存放第0代中未被釋放的物件。對於第2代,如果記憶體已滿,也會進行記憶體釋放,如果這些物件都在使用而無法釋放,就會報錯說記憶體空間不夠用。從上面可以看出,越老的物件生存機率越大。

前面已經提到,垃圾回收只回收託管堆中的記憶體資源,不回收其它資源。那其它資源該如何回收呢?這時就可以使用解構函式或Dispose()方法。

類的解構函式由帶有~字首的類名(與建構函式的相同)來宣告。在.Net中使用的解構函式(由System.Object提供)叫做Finalize(),但這不是我們用於宣告解構函式的名稱。如下所示:

class MyClass

{

~MyClass()

{

//Destructor body

}

}

 當進行垃圾回收時,就執行解構函式中的程式碼,釋放資源。在呼叫這個解構函式之後,還將隱式地呼叫基類的解構函式,包括System.Object根類中的Finalize()呼叫。這個技術可以讓.NET Framework確保呼叫Finalize(),因為重寫Finalize()的指基類呼叫需要顯式的執行,這是有潛在危險的。上面的程式碼在C++中叫解構函式,在C#中叫Finalize()函式,在當前物件被垃圾回收之前會呼叫這個函式,釋放其它資源。但是我們不能手動呼叫解構函式,所以一般回收其它記憶體,都寫在Dispose()方法中。而Dispose()方法由IDisposeable介面提供,因此重寫Dispose()方法的類需要繼承這個介面。由於Dispose()方法已經可以釋放其它資源,這時就不需要再呼叫Finalize()方法了,所以在Dispose()方法裡面通過GC.SuppressFinalize(this)方法告訴程式不用呼叫Finalize()方法了。

再說關於弱引用,先看下面的程式碼:

class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            p.Age = 18;
            p = null;

            Console.ReadKey();
        }
    }

    public class Person
    {
        public int Age { get; set; }
    }
在上面的程式碼中,我們先建立了一個Person類的物件,然後給它賦值。當程式執行到p=null的時候,如果再想用p指向程式開始建立的物件(new Person()),是不可能的,因為這個物件雖然存在於記憶體中,但沒有任何引用指向它,無法找到它的地址。如果想要得到這個物件,可以使用弱引用。如下所示:
static void Main(string[] args)
        {            
            Person p = new Person();  //建立一個物件
            p.Age = 18;
            WeakReference wr = new WeakReference(p);  //把這個物件傳給弱引用
            p = null;     //對這個物件進行垃圾回收

            object pnew = wr.Target;   //獲取弱引用中剛才建立的物件
            if (pnew != null)
            {
                Console.WriteLine(((Person)pnew).Age);
            }
            else
            {
                Console.WriteLine("物件已經被回收!");
            }

            Console.ReadKey();
        }
執行程式,顯示的字串是18。使用弱引用需要建立一個WeakReference物件,並且把要進行回收的物件傳過去,這樣在垃圾回收之後,我們仍然可以得到剛才建立的物件。這與直接讓另外一個Person型別的變數p1來指向new Person()不同,用p1指向new Person()不能物件這個物件進行回收,而使用弱引用卻可以把這個物件回收,釋放資源。

如下所示,我們在上面的程式碼中加一個回收器:

static void Main(string[] args)
        {            
            Person p = new Person();  //建立一個物件
            p.Age = 18;
            WeakReference wr = new WeakReference(p);  //把這個物件傳給弱引用
            p = null;     //對這個物件進行垃圾回收

            GC.Collect();   //手動呼叫系統的垃圾回收器
            object pnew = wr.Target;   //獲取弱引用中剛才建立的物件
            if (pnew != null)
            {
                Console.WriteLine(((Person)pnew).Age);
            }
            else
            {
                Console.WriteLine("物件已經被回收!");
            }

            Console.ReadKey();
        }
再次執行程式,我們會發現這個物件已經不存在了,因為它已經被當作垃圾回收處理。弱引用一般用於那些建立時比較耗時的物件。

相關推薦

C#垃圾回收和解函式以及引用

在程式中,當我們每建立一個物件,就會在記憶體中開闢一個空間,用以存放這個物件。如果建立的物件多了,記憶體就會出現不夠用的情況。這時我們就要把記憶體中不再使用的物件釋放掉,避免記憶體的佔用及程式的異常。這個過程就是垃圾回收。手動進行垃圾回收的方法是:GC.Collect();

C++使用構造和解函式管理同步鎖的一種方法Guard

在C++中,通過提供建構函式、解構函式來對處理資源的獲取、釋放。 通過C++的這種機制,我們可以很方便地處理C++中的加鎖同步機制。把鎖物件作為Guard物件的一個成員(m_lock),然後在Guard物件的構造中對m_lock進行加鎖:m_lock.acquire(),在

淺談.NET非託管資源垃圾回收與程式資源優化(引用)

程式的資源分為託管資源與非託管資源,CLR直接管理的資源就是託管資源,還有一類非託管資源如IO、Socket、AplicationContext、Regex、Stream流、資料庫操作等等,這些直接由系統管理佔用記憶體。在垃圾回收的時候,雖然垃圾回收器可以跟蹤封裝非託管資

C++】類的六大預設的成員函式 之 解函式以及建構函式和解函式的呼叫順序

                                              解構函式 一.解構函式定義               解構函式也是特殊的成員函式,他的作用和建構函式相反。 class Box { public: //建構函式 Box(i

C/C++開發】C++ 解函式以及 delete 和delete[]的整理

轉自:http://hi.baidu.com/bystander1983/item/bf0b5c12b077cfec9913d651 delete和delete[] 的區別: C++告訴我們在回收用 new 分配的單個物件的記憶體空間的時候用 delete,回收用 new[] 分配的一組物

C++學習--建構函式和解函式

建構函式是對物件進行初始化的成員函式,建構函式的名字和類的名字是一樣的,建構函式不用特定的呼叫,是在類例項化中,自動呼叫。建構函式可以有多個,也就是可以進行建構函式過載,例項化過程中會根據建構函式的引數型別、個數進行呼叫。解構函式和建構函式相反,是完成一些清理工作,比如釋放記憶體或者其他的工作。解構

C++中的虛擬函式和解函式的定義和作用

虛擬函式 定義: C++中的虛擬函式的作用是允許在派生類中重新定義與基類同名的函式,並且可以通過基類指標或引用來訪問基類和派生類中的同名函式。 虛擬函式的使用方法是: 在基類用virtual宣告成員函式為虛擬函式。這樣就可以在派生類中重新定義此函式,為它賦予新的功能,並能

C++建構函式和解函式什麼情況下會用

解構函式: 1. 物件生命週期結束,被銷燬時; 2. delete 指向物件的指標時; 3. delete 指向基類物件的指標時,其解構函式是虛擬函式; 4. 在巢狀關係中,物件A是物件B的成員,當物件B執行解構函式時,物件A的解構函式也會被呼叫; 建構函式: 1.  在宣告一個普通物件

派生類的建構函式和解函式C++學習筆記 32)

  引入繼承的目的:①派生類繼承了基類的成員,實現了原有程式碼的重用。②實現程式碼的擴充,只有在派生類中通過新增新的成員,加入新的功能,類的派生才有實際意義。   基類的建構函式和解構函式不能被繼承,在派生類中,如果對派生類新增的成員進行初始化,就需要加入派生類的建構函式,同時,對所有從基類

提高C++效能的程式設計技術筆記:建構函式和解函式+測試程式碼

物件的建立和銷燬往往會造成效能的損失。在繼承層次中,物件的建立將引起其先輩的建立。物件的銷燬也是如此。其次,物件相關的開銷與物件本身的派生鏈的長度和複雜性相關。所建立的物件(以及其後銷燬的物件)的數量與派生的複雜度成正比。 並不是說繼承根本上就是程式碼效能的絆腳石。我們必須區分全部計算開銷、

C++中的建構函式和解函式

1:建構函式 處理物件的初始化。特殊的成員函式,不需要使用者來呼叫,而是在建立物件的時候自動執行。 特點: ①與類名相同 ②沒有任何返回型別 ③定義時可以有引數,可以無引數 2:解構函式 語法 :~cl

C++中 建構函式和解函式能不能被顯示呼叫

                程式碼: view plaincopy to clipboardprint?#include <iostream>   using namespace std;     class A   {   public:       A()       {         

C++進階--建構函式和解函式中的虛擬函式

//############################################################################ /* 任何時候都不要在建構函式或解構函式中呼叫虛擬函式 */ class dog { public: string m_name

C++建構函式和解函式--從常見的面試題說起系列1

class A { public:     A(){a=0}     virtual ~A(){printf "aaa";}} private:     int a; } class B: public A { public:     B(){a=2

C#—建構函式和解函式

一、前言        作為比C更先進的語言,C#提供了更好的機制來增強程式的安全性,C#編譯器具有嚴格的型別安全檢查功能,它幾乎能找出程式中所有的語法問題,但是程式通過了編譯檢查並不表示錯誤已經不存在了,不少難以察覺的程式錯誤是由於變數沒有被正確初始化或清除造成的,而初

C++ 建構函式和解函式與virtual關鍵字

一、建構函式 1.建構函式任何時候都不可以宣告為虛擬函式,原因如下:1)虛擬函式的呼叫軍需通過虛擬函式表vtable來呼叫,虛擬函式表是儲存在記憶體空間的,在呼叫建構函式前,物件還未被實力化,也就沒有

C++知識點10:建構函式和解函式

建構函式: 生成物件時呼叫建構函式 生成派生類物件時先呼叫基類建構函式在呼叫派生類建構函式 #include <iostream> using namespace std; class base { public: base(){

C++中建構函式和解函式丟擲異常問題

一. 丟擲異常 1.1 丟擲異常(也稱為拋棄異常)即檢測是否產生異常,在C++中,其採用throw語句來實現,如果檢測到產生異常,則丟擲異常。 該語句的格式為: throw 表示式; 如果在try語句塊的程式段中(包括在其中呼叫的函式)發現了異常,且拋棄了

C++派生類的建構函式和解函式執行順序及其構造形式

    在C++程式設計師的面試中,經常會出現派生類與基類的建構函式、解構函式的執行順序。其實這是一個很基本的問題,沒有什麼難度,只需要記住就OK了。      1.派生類的建構函式和解構函式的執行順序     首先執行基類的建構函式,隨後執行派生類的建構函式,當撤銷派生類物件時,限執行派生類的解構函

C++:面試時應該實現的string類(建構函式、拷貝建構函式、賦值運算子過載和解函式

一、string類的4個基本函式是什麼? 建構函式 拷貝建構函式 賦值運算子過載 解構函式 二、函式實現 1.建構函式 String(char* pStr = " ")