net内存回收與Dispose﹐Close﹐Finalize方法 - 新聞資訊 - 雲南小程序開發|雲南軟件開發|雲南網站建設-昆明融晨信息技術有限公司

159-8711-8523

雲南網建設/小程序開發/軟件開發

知識

不(bù)管是(shì)網站,軟件還是(shì)小程序,都要(yào / yāo)直接或間接能爲(wéi / wèi)您産生價值,我們在(zài)追求其視覺表現的(de)同時(shí),更側重于(yú)功能的(de)便捷,營銷的(de)便利,運營的(de)高效,讓網站成爲(wéi / wèi)營銷工具,讓軟件能切實提升企業内部管理水平和(hé / huò)效率。優秀的(de)程序爲(wéi / wèi)後期升級提供便捷的(de)支持!

您當前位置>首頁 » 新聞資訊 » 技術分享 >

net内存回收與Dispose﹐Close﹐Finalize方法

發表時(shí)間:2020-10-18

發布人(rén):融晨科技

浏覽次數:83

 一. net的(de)對象使用一般分爲(wéi / wèi)三種情況﹕ 

 
1.創建對象 
2.使用對象 
3.釋放對象 
 
 
二.創建對象 
 
1.創建對象實際分爲(wéi / wèi)兩個(gè)步驟﹕變量類型宣告和(hé / huò)初始化對象 
 
2.變量類型宣告(declare),如﹕ 
複制  保存
FileStream fs
 
這(zhè)行代碼會在(zài)當前的(de)變量作用域空間(棧或堆)裏建立一個(gè)叫做fs的(de)變量﹐至少四個(gè)字節吧(因爲(wéi / wèi)要(yào / yāo)存一個(gè)對象的(de)地(dì / de)址) 
 
3.初始化對象 
對象在(zài)使用(調用其方法或屬性)前﹐必須進行初始化。 
如﹕ 
複制  保存
fs = new FileStream(@"C: est.txt",FileMode.OpenOrCreate);
 
這(zhè)行代碼會分成3個(gè)步驟﹕ 
 
a.在(zài)托管堆中分配一塊内存﹐其大(dà)小等于(yú)FileStream中所有字段(當然不(bù)包括靜态的(de))的(de)内存總和(hé / huò)加上(shàng)MS認爲(wéi / wèi)需要(yào / yāo)的(de)其它東東。 
b.初始化對象的(de)字段(值類型的(de)把其位全部初始化成0,對象初始化爲(wéi / wèi)null﹐當然string是(shì)一個(gè)例外﹐它被初始化成空字符串) 
c.調用FileStream相應的(de)構造器﹐這(zhè)裏會初始化一個(gè)非托管資源(文件)的(de)私有字段。 
 
 
三.使用對象 
 
使用對象就(jiù)沒什麽講的(de)﹐就(jiù)是(shì)調用對象的(de)方法(或屬性等)來(lái)完成某個(gè)功能當然爲(wéi / wèi)了(le/liǎo)釋放對象而(ér)調用的(de)方法其範疇應不(bù)屬于(yú)此類中(現在(zài)提到(dào)的(de)Finalize等) 
 
 
四.釋放對象 
 
1.釋放對象也(yě)就(jiù)是(shì)說(shuō)這(zhè)個(gè)對象我已經不(bù)需要(yào / yāo)了(le/liǎo)﹐現在(zài)我要(yào / yāo)把其釋放﹐以(yǐ)便把其在(zài)堆上(shàng)所占用的(de)内存空間給收回來(lái)(當然變量名的(de)内存空間就(jiù)不(bù)需要(yào / yāo)管了(le/liǎo)﹐因爲(wéi / wèi)它會随其作用域自動消失) 
 
2. .net自動進行内存管理﹐也(yě)就(jiù)是(shì)說(shuō)當它判斷一個(gè)對象沒有用了(le/liǎo)(當然有自己的(de)算法)﹐它就(jiù)會将其内存給自動收回來(lái)﹐但是(shì)其收回的(de)時(shí)間一般不(bù)确定(當.net認爲(wéi / wèi)内存緊張時(shí)﹐它就(jiù)會開始) 
 
BTW:其實我們就(jiù)是(shì)想自己收回對象的(de)内存也(yě)不(bù)可能﹐因爲(wéi / wèi)MS沒有提供途徑(GC.Collect也(yě)是(shì)啓動.net的(de)内存收集功能) 
 
 
五.第一個(gè)結論 
 
在(zài)net中使用對象很簡單﹐創建對象之(zhī)後直接使用就(jiù)可以(yǐ)了(le/liǎo)﹐不(bù)用了(le/liǎo)也(yě)不(bù)要(yào / yāo)去管它﹐垃圾收集器會幫你把内存要(yào / yāo)回來(lái)的(de)。 
 
 
六.例外 
 
當對象的(de)成員引用了(le/liǎo)一個(gè)非托管資源時(shí)(不(bù)在(zài)托管堆上(shàng)分配的(de)内存或資源﹐像文件﹐數據庫連接等等)﹐下面以(yǐ)一個(gè)例子(zǐ)來(lái)說(shuō)明﹕ 
System.IO.FileStream類别﹐這(zhè)是(shì).net基本類庫提供的(de)一個(gè)非托管資源(文件)封裝對象(用Reflector工具反編譯mscorlib.dll可見其代碼) 
 
1.FileStream毫無疑問封裝了(le/liǎo)一個(gè)非托管資源 
 
觀其源代碼發現有這(zhè)樣一個(gè)私有成員﹕ 
複制  保存
private SafeFileHandle _handle;
 
通過構造器調用的(de)Init方法可以(yǐ)發現這(zhè)個(gè)成員的(de)初始化代碼﹕ 
複制  保存
this._handle = Win32Native.SafeCreateFile(text2, num1, share, secAttrs, mode, num2,  
Win32Native.NULL);
 
而(ér)後者實際上(shàng)就(jiù)是(shì)kernel32.dll中的(de)CreateFile方法﹐它返回一個(gè)HANDLE(即非托管資源引用) 
 
2.我們先來(lái)使用這(zhè)個(gè)類别﹕ 
複制  保存
using System;
using System.IO;
 
public class TestFileStream
{
    public static void Main(string[] args)
    {
        //創建一個(gè)FileStream對象
        FileStream fs = new FileStream(@"C: est.txt", FileMode.OpenOrCreate);
        Console.WriteLine("您可以(yǐ)嘗試在(zài)系統中删除c盤下的(de)test.txt(回車鍵繼續)");
        //暫停程序執行﹐并嘗試在(zài)系統中删除那個(gè)文件
        Console.ReadLine();
 
        //删除文件測試
        try
        {
            File.Delete(@"c: est.txt");
        }
        catch (IOException ex)
        {
            Console.WriteLine("[Error]程序删除文件失敗﹕{0}", ex.Message);
        }
    }
}
 
3.在(zài)程序挂起時(shí)(Console.ReadLine等待輸入)﹐删除文件會失敗﹐很容易理解﹐因爲(wéi / wèi)文件打開後沒有将其關閉﹐系統不(bù)知道(dào)這(zhè)個(gè)文件是(shì)否還有用﹐所以(yǐ)幫我們保護這(zhè)個(gè)文件(理所當然﹐那個(gè)非托管資源所使用的(de)内存還被程序占用着) 
4.但是(shì)在(zài)程序執行完後﹐我們再嘗試删除文件﹐成功﹗爲(wéi / wèi)什麽?(fs不(bù)是(shì)沒有關閉那個(gè)SafeFileHandle嗎?) 
當然您可以(yǐ)說(shuō)﹐windows操作系統在(zài)一個(gè)進程結束後會自動回收其資源﹐沒錯(但是(shì)如果是(shì)com就(jiù)慘了(le/liǎo)﹐因爲(wéi / wèi)com是(shì)存在(zài)于(yú)自己的(de)獨立進程内﹐而(ér)操作系統不(bù)負責這(zhè)個(gè)  )﹐不(bù)過這(zhè)裏不(bù)是(shì)因爲(wéi / wèi)windows操作系統的(de)功能﹐而(ér)是(shì).net垃圾收集器幫的(de)忙。 
 
5.看下面這(zhè)個(gè)例子(zǐ) 
複制  保存
using System;
using System.IO;
 
public class TestFileStream
{
    public static void Main(string[] args)
    {
        //創建一個(gè)FileStream對象
        FileStream fs = new FileStream(@"C: est.txt", FileMode.OpenOrCreate);
        Console.WriteLine("您可以(yǐ)嘗試在(zài)系統中删除c盤下的(de)test.txt(回車鍵繼續)");
        //暫停程序執行﹐并嘗試在(zài)系統中删除那個(gè)文件
        Console.ReadLine();
 
        /*進行垃圾收集*/
        GC.Collect();
        Console.WriteLine("再删一下試試");
        Console.ReadLine();
    }
}
 
6.注意中間那行代碼: 
複制  保存
GC.Collect();
 
這(zhè)是(shì)強制要(yào / yāo).net垃圾收集器進行垃圾收集。 
我們再去嘗試删除test.txt﹐居然可以(yǐ)被删除了(le/liǎo)﹐爲(wéi / wèi)什麽呀?(fs不(bù)是(shì)沒有關閉那個(gè)SafeFileHandle嗎?)﹐讓我細細道(dào)來(lái)﹕ 
 
7.我們首先了(le/liǎo)解一下.net垃圾收集器進行垃圾收集的(de)四種時(shí)機(參見﹕.net框架程序設計 李建忠譯) 
 
a.最常見的(de)﹕當.net覺得合适時(shí)﹐例如它感到(dào)内存緊張了(le/liǎo)(朮語稱爲(wéi / wèi)﹕0代對象充滿) 
b.微軟強烈不(bù)建議使用的(de)﹕GC的(de)Collect方法調用(就(jiù)是(shì)我們上(shàng)面用的(de)這(zhè)種啦﹐因爲(wéi / wèi)會降低性能﹐會挂起進程, 等等﹐反正聽微軟的(de)吧。當然某些時(shí)候可以(yǐ)用﹐就(jiù)像我上(shàng)面用來(lái)測試的(de)代碼﹐呵呵...) 
c.應用程序域卸載時(shí)(AppDomain) 
d.CLR被關閉時(shí) 
 
8.現在(zài)我們可以(yǐ)明白第1個(gè)例子(zǐ)爲(wéi / wèi)什麽在(zài)程序結束後文件可以(yǐ)被删除﹐因爲(wéi / wèi)CLR被關閉時(shí)﹐.net執行了(le/liǎo)垃圾收集(也(yě)就(jiù)是(shì)等同于(yú)第二個(gè)例子(zǐ)的(de)GC.Collect()代碼) 
 
9.所以(yǐ)現在(zài)所有的(de)問題都集中到(dào)垃圾收集上(shàng)面﹐它做了(le/liǎo)什麽? 
 
a.垃圾收集器在(zài)判斷一個(gè)對象不(bù)會再被引用到(dào)後﹐就(jiù)開始對它進行垃圾收集(即回收内存) 
b.清空内存(即把托管堆中的(de)内存收回來(lái)) 
c.但是(shì)對象的(de)有些字段引用到(dào)了(le/liǎo)非托管資源怎麽辦?如FileStream的(de)_handle 
d.所以(yǐ)我們必須告訴垃圾收集器﹐在(zài)你回收我的(de)内存之(zhī)前﹐先幫我執行一個(gè)方法來(lái)收回我的(de)非托管資源﹐以(yǐ)免托管堆的(de)内存被你回收了(le/liǎo)﹐而(ér)我引用的(de)非托管資源的(de)内存卻被洩漏了(le/liǎo)。 
e.這(zhè)個(gè)方法就(jiù)是(shì)Finalize()﹐也(yě)就(jiù)是(shì)C#的(de) ~ClassName() 方法(同C++中的(de)析構語法) 
f.所以(yǐ)一個(gè)對象如果存在(zài)Finalize方法時(shí)﹐垃圾收集器在(zài)收回它的(de)内存之(zhī)前就(jiù)會自動調用這(zhè)個(gè)方法 
g.這(zhè)樣我們就(jiù)可以(yǐ)把那些東東(非托管資源)給清理幹淨了(le/liǎo) 
 
由此看來(lái)﹐垃圾收集器提供的(de)這(zhè)種機制就(jiù)是(shì)爲(wéi / wèi)了(le/liǎo)更好的(de)完善.net的(de)自動内存管理的(de)功能﹐讓我們也(yě)可以(yǐ)參與到(dào)垃圾收集中去 
 
10.我們再來(lái)看看GC.Collect()這(zhè)行代碼或CLR關閉時(shí).Net做了(le/liǎo)什麽﹕ 
 
a.垃圾收集器啓動﹐發現fs引用的(de)那個(gè)對象已經沒用了(le/liǎo)(當然CLR關閉時(shí)才不(bù)管你有沒有用﹐通通回收)﹐于(yú)是(shì)對它進行内存回收 
b.發現fs的(de)類型﹕FileStream提供了(le/liǎo)Finalize方法﹐于(yú)是(shì)先調用這(zhè)個(gè)方法 
(以(yǐ)下通過Reflector繼續) 
c.Finalize方法中有 this._handle.Dispose()代碼﹐于(yú)是(shì)調用SafeHandler.Dispose() 
d.接着轉到(dào)(當然好多個(gè)圈﹐您悠着點...)SafeFileHandle.ReleaseHandle方法﹐發現代碼﹕Win32Native.CloseHandle() (即關閉非托管資源--文件HANDLE) 
 
真相大(dà)白﹕原來(lái)是(shì)垃圾收集器幫我們關閉了(le/liǎo)那個(gè)非托管資源(當然還是(shì)通過我們自己寫的(de)Finalize方法)﹐因此後面就(jiù)可以(yǐ)删除文件了(le/liǎo)。 
 
11.有人(rén)會問﹕好像我們平時(shí)在(zài)使用FileStream對象時(shí)﹐沒這(zhè)麽複雜呀? 
答﹕Very Good! 
 
一部分人(rén)﹕是(shì)因爲(wéi / wèi)大(dà)家都和(hé / huò)我的(de)例1一樣有好運氣﹐那個(gè)C盤下的(de)test.txt文件自從被創建後﹐我壓根就(jiù)不(bù)會再去用它﹐管它這(zhè)部分資源有沒有被洩漏﹐有沒有被鎖定﹐最後程序結束時(shí)﹐被垃圾收集器幫了(le/liǎo)忙﹐把忘了(le/liǎo)關閉的(de)文件HANDLE給收回來(lái)了(le/liǎo)。 
 
剩下的(de)一部分人(rén)﹕在(zài)程序裏埋下了(le/liǎo)一顆"啞彈"﹐不(bù)知什麽時(shí)候會爆炸﹐就(jiù)像我例子(zǐ)中的(de)File.Delete方法就(jiù)出(chū)現了(le/liǎo)異常。 
 
(不(bù)過我覺得)絕大(dà)多數人(rén)﹕是(shì)在(zài)看了(le/liǎo)很多諸如.net編程忠告﹐Microsoft強烈建議﹐MSDN标準做法等等等等( 還有我這(zhè)篇blog﹐呵呵)之(zhī)後﹐知道(dào)了(le/liǎo)在(zài)使用如FileStream,SqlConnection這(zhè)些東東時(shí)﹐必須将其Close。 
 
12.Close與Dispose 
查看我們那兩個(gè)例子(zǐ)的(de)代碼﹐都是(shì)不(bù)标準的(de)﹐正确做法應該在(zài)使用完那個(gè)FileStream後﹐調用fs.Close()将其關閉﹐以(yǐ)保證資源的(de)安全。 
 
附﹕正确做法 
複制  保存
using System;
using System.IO;
 
public class TestFileStream
{
    public static void Main(string[] args)
    {
        //創建一個(gè)FileStream對象
        FileStream fs = new FileStream(@"C: est.txt", FileMode.OpenOrCreate);
 
        /*在(zài)用完FileStream後關閉*/
        fs.Close();
        //删除文件測試
        try
        {
            File.Delete(@"c: est.txt");
        }
        catch (IOException ex)
        {
            Console.WriteLine("[Error]程序删除文件失敗﹕{0}", ex.Message);
        }
    }
}
 
13.有人(rén)舉手﹐講這(zhè)麽多﹐早告訴我調用fs.Close不(bù)就(jiù)得了(le/liǎo)。 
哥們﹐fs.Close()方法是(shì)由您寫的(de)﹐調不(bù)調用﹐手在(zài)您身上(shàng)﹐您不(bù)調用的(de)話﹐哪天程序出(chū)了(le/liǎo)問題﹐您有會叫﹕微軟真垃圾﹐.net真不(bù)穩定﹐還是(shì)java好﹐安全﹐可靠...    爲(wéi / wèi)防您的(de)國(guó)罵﹐MS隻好在(zài)垃圾收集中加這(zhè)一款﹐以(yǐ)防不(bù)測... 
 
14.Dispose模式 
認真查看.net類庫中的(de)那些基本類别﹐凡是(shì)有Finalize方法的(de)類别﹐基本上(shàng)都提供了(le/liǎo)諸如Dispose,Close,Dispose(bool)等方法(FileStream也(yě)不(bù)例外) 
 
15.其實不(bù)管是(shì)Dispose,Close,Finalize方法﹐最終應該都是(shì)執行相同的(de)代碼 
區别﹕ 
Finalize方法﹕隻能由微軟調用 
Dispose和(hé / huò)Close方法﹕提供給您調用 
因此在(zài)您使用完那些類别後﹐那就(jiù)直接調用Close吧(沒有Close﹐再調用Dispose方法)﹐當然萬一您忘了(le/liǎo)﹐也(yě)别擔心﹐還有垃圾收集器幫您墊後。 
 
 
七.第二個(gè)結論﹕ 
 
1.在(zài)您開發一個(gè)封裝非托管資源(即類中的(de)字段引用到(dào)了(le/liǎo)非托管資源)的(de)類别時(shí)﹕ 
 
A:強烈建議您提供Finalize方法進行非托管資源的(de)釋放﹐.net垃圾收集器不(bù)會幫您自動回收那部分資源﹐而(ér)是(shì)通過調用您的(de)Finalize方法來(lái)幫您釋放。(這(zhè)樣可以(yǐ)保證﹕在(zài)使用您類别的(de)那位程序員忘了(le/liǎo)手動回收内存時(shí)﹐還可通過垃圾收集器來(lái)補救) 
 
B.強烈建議您提供一個(gè)Close或Dispose方法﹐以(yǐ)便使用您類别的(de)程序員可以(yǐ)手動釋放您的(de)類别中的(de)非托管資源。(參見.net框架程序設計 自動内存管理一章實現Dispose模式) 
 
C.如果類别封裝了(le/liǎo)像FileStream這(zhè)樣的(de)對象(即對非托管資源的(de)再次封裝)時(shí)﹐一般也(yě)應該提供一 個(gè)Close或Dispose方法﹐除非您的(de)這(zhè)個(gè)成員保證在(zài)每次使用後﹐都被正常的(de)關閉﹐即對調用者透明。 
 
 
2.在(zài)您使用一個(gè)封裝非托管資源的(de)類别時(shí)﹕ 
 
A:強烈建議您在(zài)明确知道(dào)這(zhè)個(gè)類别沒有用之(zhī)後﹐調用其提供的(de)Close或Dispose方法手動釋放其非托管資源的(de) 内存。有道(dào)是(shì)﹕有借有還﹐再借不(bù)難;借了(le/liǎo)不(bù)還﹐再借休想~~ 
 
B:注意在(zài)手動釋放後﹐不(bù)要(yào / yāo)再調用該對象的(de)相關方法了(le/liǎo)﹐因爲(wéi / wèi)對象已經損毀了(le/liǎo) 
 
再次BTW:不(bù)管是(shì)Finalize﹐Close還是(shì)Dispose﹐您都無法顯式釋放托管堆内存﹐它們永遠是(shì)微軟的(de)"私人(rén)财産 "﹕) 
 
 
有人(rén)議論: 
 
.net 不(bù)要(yào / yāo)把對象 = null 的(de);  
一位在(zài)一般情況下.net的(de)的(de)一個(gè)變量如  
FileStream fs = new FileStream(@"C:\test.txt", FileMode.OpenOrCreate);  
 
這(zhè)個(gè) fs 類似c 語言裏的(de)指針,隻是(shì)一個(gè)地(dì / de)址而(ér)已  
= null 是(shì)沒啥用的(de)  
 
如果等于(yú)null 反倒影響gc 回收了(le/liǎo)  
 
還有.net Windows 程序  
和(hé / huò) ASP.NET 下 GC 的(de)回收也(yě)許會有些不(bù)同  
所以(yǐ)一個(gè)這(zhè)樣的(de)列子(zǐ)不(bù)會完全說(shuō)明問題的(de)。  
 
還有如個(gè)cpu占有率比較高的(de)情況下 GC 也(yě)許回收對象很慢  
要(yào / yāo)比正常情況下慢很多。  
 
原則上(shàng)還是(shì)、能 Dispose 的(de)類就(jiù)要(yào / yāo) Dispose  
類似FileStream 的(de)對象如果不(bù)在(zài)後面的(de)代碼中使用,不(bù)用 close 直接  
Dispose 即可,Dispose 隐含 close 的(de)其實  
數據連接對象也(yě)是(shì)推薦使用 using 代碼塊自動釋放以(yǐ)防止中途出(chū)現異常
 
雲南軟件開發公司中的(de)佼佼者,緻力定制軟件開發,雲南軟件開發請聯系,電話:15987118523。
 
昆明軟件開發公司中的(de)佼佼者,緻力定制軟件開發,昆明軟件開發請聯系,電話:15987118523。

相關案例查看更多