茄子在线看片免费人成视频,午夜福利精品a在线观看,国产高清自产拍在线观看,久久综合久久狠狠综合

    <s id="ddbnn"></s>
  • <sub id="ddbnn"><ol id="ddbnn"></ol></sub>

  • <legend id="ddbnn"></legend><s id="ddbnn"></s>

    .NET垃圾回收:非托管資源
    來源:易賢網 閱讀:1924 次 日期:2015-04-02 13:11:22
    溫馨提示:易賢網小編為您整理了“.NET垃圾回收:非托管資源”,方便廣大網友查閱!

    前面一篇文章介紹了垃圾回收的基本工作原理,垃圾回收器并不是可以管理內存中的所有資源。對于所有的托管資源都將有.NET垃圾回收機制來釋放,但是,對于一些非托管資源,我們就需要自己編寫代碼來清理這類資源了。

    其實在C#開發(fā)中,大部分資源都可以通過.NET垃圾回收機制進行回收,只用當我們使用非托管資源(原始的操作系統(tǒng)文件句柄,原始的非托管數(shù)據庫連接,非托管內存等等)的時候,我們才需要實現(xiàn)自己的資源清理代碼。

    .NET提供了兩種釋放非托管資源的方式,類型自己的Finalize方法和IDisposable接口的Dispose方法。

    下面就來看看這兩個跟垃圾回收相關的方法。

    Finalize方法

    在.NET的基類System.Object中,定義了名為Finalize()的虛方法,這個方法默認什么都不做。

    我們可以為自定義的類型重寫Finalize方法,在該方法中加入必要的非托管資源清理邏輯。當要從內存中刪除這個類型的對象時,垃圾回收器會調用對象的Finalize方法。所以,無論.NET進行一次自發(fā)的垃圾回收,還是我們通過GC.Collect()進行強制垃圾回收,F(xiàn)inalize方法總是會被調用。另外,當承載應用程序的AppDomain從內存中移除時,同樣會調用Finalize方法。

    重寫Finalize方法

    假設我們現(xiàn)在有一個使用非托管資源的類型,那么我們就需要重寫Finalize方法來進行非托管資源的清理,但是當通過下面的方式重寫Finalize方法的時候,我們會得到一個編譯錯誤。

    class MyResourceWrapper

    {

    protected override void Finalize()

    {

    }

    }

    名單

    其實,當我們想要重寫Finalize方法時,C#為我們提供了(類似C++)析構函數(shù)語法(C#終結器)來重寫該方法。C#終結器和構造函數(shù)語法類似,方法名稱都和類型名稱一樣;不同的是,終結器具有~前綴,并且不能使用訪問修飾符,不接受參數(shù),也不能重載,所以一個類只能有一個終結器。

    class MyResourceWrapper

    {

    ~MyResourceWrapper()

    {

    Console.WriteLine("release unmanaged resources");

    Console.Beep();

    }

    }

    之所以C#只支持這種方式進行Finalize方法的重寫,是因為C#編譯器會為Finalize方法隱式地加入一些必需的基礎代碼。下面就是我們通過ILSpy查看到了IL代碼,F(xiàn)inalize方法作用域內的代碼被放在了一個try塊中,然后不管在try塊中是否遇到異常,finally塊保證了Finalize方法總是能夠被執(zhí)行。

    .method family hidebysig virtual

    instance void Finalize () cil managed

    {

    // Method begins at RVA 0x2050

    // Code size 31 (0x1f)

    .maxstack 1

    .try

    {

    IL_0000: nop

    IL_0001: ldstr "release unmanaged resources"

    IL_0006: call void [mscorlib]System.Console::WriteLine(string)

    IL_000b: nop

    IL_000c: call void [mscorlib]System.Console::Beep()

    IL_0011: nop

    IL_0012: nop

    IL_0013: leave.s IL_001d

    } // end .try

    finally

    {

    IL_0015: ldarg.0

    IL_0016: call instance void [mscorlib]System.Object::Finalize()

    IL_001b: nop

    IL_001c: endfinally

    } // end handler

    IL_001d: nop

    IL_001e: ret

    } // end of method MyResourceWrapper::Finalize

    當我們執(zhí)行下面代碼時,我們就可以聽到系統(tǒng)蜂鳴聲,像我們前面介紹的一樣AppDomain被移除內存,類型終結器將被調用。

    static void Main(string[] args)

    {

    MyResourceWrapper mr = new MyResourceWrapper();

    }

    Finalize的工作機制

    Finalize的工作機制還是比較復雜的,這里只是簡單的介紹,更多的原理大家可以自己網上查查。

    當在托管堆上分配對象空間時,運行庫會自動確定該對象是否提供一個自定義的Finalize方法。如果是這樣,對象被標記為可終結的,同時一個指向這個對象的指針被保存在名為終結隊列的內部隊列中。終結隊列是一個由垃圾回收器維護的表,它指向每一個在從堆上刪除之前必須終結的對象。

    當垃圾回收器確定到了從內存中釋放一個對象的時間時,它檢查終結隊列上的每一個項,并將對象從堆上復制到另一個稱作終結可達表(finalization reachable table的托管結構上。此時,下一個垃圾回收時將產生另外一個線程,為每一個在可達表中的對象調用Finalize方法。因此,為了真正終結一個對象,至少要進行兩次垃圾回收。

    從上面可以看到,F(xiàn)inalize方法的調用是相當消耗資源的。Finalize方法的作用是保證.NET對象能夠在垃圾回收時清理非托管資源,如果創(chuàng)建了一個不使用非托管資源的類型,實現(xiàn)終結器是沒有任何作用的。所以說,如果沒有特殊的需求應該避免重寫Finalize方法。

    IDisposable接口

    當垃圾回收生效時,可以利用終結器來釋放非托管資源。然而,很多非托管資源都非常寶貴(如數(shù)據庫和文件句柄),所以它們應該盡可能快的被清除,而不能依靠垃圾回收的發(fā)生。除了重寫Finalize之外,類還可以實現(xiàn)IDisposable接口,然后在代碼中主動調用Dispose方法來釋放資源。

    看一個例子:

    class MyResourceWrapper:IDisposable

    {

    public void Dispose()

    {

    Console.WriteLine("release resources with Dispose");

    Console.Beep();

    }

    }

    class Program

    {

    static void Main(string[] args)

    {

    MyResourceWrapper mr = new MyResourceWrapper();

    mr.Dispose();

    }

    }

    同樣,當我們顯示的調用Dispose方法的時候,可以聽到系統(tǒng)的蜂鳴聲。

    注意,通過Dispose進行資源的釋放也是有潛在的風險的,因為Dispose方法需要被程序員顯示的調用,如果代碼中漏掉了Dispose的調用或者在Dispose調用之前產生了異常從而沒有指定Dispose,那么有些資源可能就一直留在內存中了。

    所以我們應該使用下面的方式保證Dispose方法可以被調用到:

    static void Main(string[] args)

    {

    MyResourceWrapper mr = new MyResourceWrapper();

    try

    {

    //do something wiht mr object

    }

    finally

    {

    mr.Dispose();

    }

    }

    但是,每次編寫Dispose的代碼都使用try塊會覺得很麻煩,還好C#中,我們可以重用using關鍵字來簡化Dispose的調用。

    重用using關鍵字

    在C#中,using語句提供了一個高效的調用對象Dispose方法的方式。對于任何IDispose接口的類型,都可以使用using語句,而對于那些沒有實現(xiàn)IDisposable接口的類型,使用using語句會導致一個編譯錯誤。

    static void Main(string[] args)

    {

    using (MyResourceWrapper mr = new MyResourceWrapper())

    {

    //do something with mr object

    }

    }

    在using語句塊結束的時候,mr實例的Dispose方法將會被自動調用。using語句不僅免除了程序員輸入Dispose調用的代碼,它還保證Dispose方法被調用,無論using語句塊順利執(zhí)行結束,還是拋出一個異常。事實上,C#編譯器為using語句自動添加了try/finally塊。我們可以看看using的IL代碼:

    .try

    {

    IL_0007: nop

    IL_0008: nop

    IL_0009: leave.s IL_001b

    } // end .try

    finally

    {

    IL_000b: ldloc.0

    IL_000c: ldnull

    IL_000d: ceq

    IL_000f: stloc.1

    IL_0010: ldloc.1

    IL_0011: brtrue.s IL_001a

    IL_0013: ldloc.0

    IL_0014: callvirt instance void [mscorlib]System.IDisposable::Dispose()

    IL_0019: nop

    IL_001a: endfinally

    } // end handler

    Dispose和Finalize的結合

    從前面的介紹了解到,F(xiàn)inalize可以通過垃圾回收進行自動的調用,而Dispose需要被代碼顯示的調用,所以,為了保險起見,對于一些非托管資源,還是有必要實現(xiàn)終結器的。也就是說,如果我們忘記了顯示的調用Dispose,那么垃圾回收也會調用Finalize,從而保證非托管資源的回收。

    其實,MSDN上給我們提供了一種很好的模式來實現(xiàn)IDisposable接口來結合Dispose和Finalize,例如下面的代碼:

    class MyResourceWrapper:IDisposable

    {

    private bool IsDisposed=false;

    public void Dispose()

    {

    Dispose(true);

    //tell GC not invoke Finalize method

    GC.SuppressFinalize(this);

    }

    protected void Dispose(bool Disposing)

    {

    if(!IsDisposed)

    {

    if(Disposing)

    {

    //clear managed resources

    }

    //clear unmanaged resources

    }

    IsDisposed=true;

    }

    ~MyResourceWrapper()

    {

    Dispose(false);

    }

    }

    在這個模式中,void Dispose(bool Disposing)函數(shù)通過一個Disposing參數(shù)來區(qū)別當前是否是被Dispose()調用。如果是被Dispose()調用,那么需要同時釋放托管和非托管的資源。如果是被終結器調用了,那么只需要釋放非托管的資源即可。Dispose()函數(shù)是被其它代碼顯式調用并要求釋放資源的,而Finalize是被GC調用的。

    另外,由于在Dispose()中已經釋放了托管和非托管的資源,因此在對象被GC回收時再次調用Finalize是沒有必要的,所以在Dispose()中調用GC.SuppressFinalize(this)避免重復調用Finalize。同樣,因為IsDisposed變量的存在,資源只會被釋放一次,多余的調用會被忽略。

    所以這個模式的優(yōu)點可以總結為:

    如果沒有顯示的調用Dispose(),未釋放托管和非托管資源,那么在垃圾回收時,還會執(zhí)行Finalize(),釋放非托管資源,同時GC會釋放托管資源

    如果調用了Dispose(),就能及時釋放了托管和非托管資源,那么該對象被垃圾回收時,就不會執(zhí)行Finalize(),提高了非托管資源的使用效率并提升了系統(tǒng)性能

    總結

    本文介紹了.NET垃圾回收中兩個相關的方法:Dispose和Finalize。Finalize的目的是用于釋放非托管的資源,而Dispose是用于釋放所有資源,包括托管的和非托管的。

    Dispose需要在代碼中進行顯示的調用,而Finalize則是由垃圾回收自動調用,為了更有效的結合Dispose和Finalize,文中還介紹了MSDN中給出的實現(xiàn)IDisposable接口的一個模式。

    更多信息請查看IT技術專欄

    更多信息請查看技術文章
    易賢網手機網站地址:.NET垃圾回收:非托管資源

    2026上岸·考公考編培訓報班

    • 報班類型
    • 姓名
    • 手機號
    關于我們 | 聯(lián)系我們 | 人才招聘 | 網站聲明 | 網站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 新媒體/短視頻平臺 | 手機站點 | 投訴建議
    工業(yè)和信息化部備案號:滇ICP備2023014141號-1 云南省教育廳備案號:云教ICP備0901021 滇公網安備53010202001879號 人力資源服務許可證:(云)人服證字(2023)第0102001523號
    聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關注公眾號:hfpxwx
    咨詢QQ:1093837350(9:00—18:00)版權所有:易賢網