国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

.net內(nèi)存回收與Dispose﹐Close﹐Finalize方法

2019-11-18 16:49:53
字體:
供稿:網(wǎng)友

.net內(nèi)存回收與Dispose﹐Close﹐Finalize方法

一. net的對象使用一般分為三種情況﹕

1.創(chuàng)建對象
2.使用對象
3.釋放對象

二.創(chuàng)建對象
1.創(chuàng)建對象實際分為兩個步驟﹕變量類型宣告和初始化對象

2.變量類型宣告(declare),如﹕

FileStream fs
這行代碼會在當(dāng)前的變量作用域空間(棧或堆)里建立一個叫做fs的變量﹐至少四個字節(jié)吧(因為要存一個對象的地址)

3.初始化對象
對象在使用(調(diào)用其方法或?qū)傩?前﹐必須進(jìn)行初始化。
如﹕


fs = new FileStream(@"C:/test.txt",FileMode.OpenOrCreate);
這行代碼會分成3個步驟﹕
a.在托管堆中分配一塊內(nèi)存﹐其大小等于FileStream中所有字段(當(dāng)然不包括靜態(tài)的)的內(nèi)存總和加上MS認(rèn)為需要的其它東東。
b.初始化對象的字段(值類型的把其位全部初始化成0,對象初始化為null﹐當(dāng)然string是一個例外﹐它被初始化成空字符串)
c.調(diào)用FileStream相應(yīng)的構(gòu)造器﹐這里會初始化一個非托管資源(文件)的私有字段。

三.使用對象
使用對象就沒什么講的﹐就是調(diào)用對象的方法(或?qū)傩缘?來完成某個功能當(dāng)然為了釋放對象而調(diào)用的方法其范疇?wèi)?yīng)不屬于此類中(現(xiàn)在提到的Finalize等)

四.釋放對象
1.釋放對象也就是說這個對象我已經(jīng)不需要了﹐現(xiàn)在我要把其釋放﹐以便把其在堆上所占用的內(nèi)存空間給收回來(當(dāng)然變量名的內(nèi)存空間就不需要管了﹐因為它會隨其作用域自動消失)

2. .net自動進(jìn)行內(nèi)存管理﹐也就是說當(dāng)它判斷一個對象沒有用了(當(dāng)然有自己的算法)﹐它就會將其內(nèi)存給自動收回來﹐但是其收回的時間一般不確定(當(dāng).net認(rèn)為內(nèi)存緊張時﹐它就會開始)

BTW:其實我們就是想自己收回對象的內(nèi)存也不可能﹐因為MS沒有提供途徑(GC.Collect也是啟動.net的內(nèi)存收集功能)

五.第一個結(jié)論
在net中使用對象很簡單﹐創(chuàng)建對象之后直接使用就可以了﹐不用了也不要去管它﹐垃圾收集器會幫你把內(nèi)存要回來的。

六.例外
當(dāng)對象的成員引用了一個非托管資源時(不在托管堆上分配的內(nèi)存或資源﹐像文件﹐數(shù)據(jù)庫連接等等)﹐下面以一個例子來說明﹕
System.IO.FileStream類別﹐這是.net基本類庫提供的一個非托管資源(文件)封裝對象(用Reflector工具反編譯mscorlib.dll可見其代碼)

1.FileStream毫無疑問封裝了一個非托管資源

觀其源代碼發(fā)現(xiàn)有這樣一個私有成員﹕


PRivate SafeFileHandle _handle;
 通過構(gòu)造器調(diào)用的Init方法可以發(fā)現(xiàn)這個成員的初始化代碼﹕


this._handle = Win32Native.SafeCreateFile(text2, num1, share, secAttrs, mode, num2, 
Win32Native.NULL);
 而后者實際上就是kernel32.dll中的CreateFile方法﹐它返回一個HANDLE(即非托管資源引用)

2.我們先來使用這個類別﹕

 

 1using System;
 2using System.IO;
 3
 4public class TestFileStream
 5{
 6    public static void Main(string[] args)
 7    {   
 8           //創(chuàng)建一個FileStream對象
 9        FileStream fs = new FileStream(@"C:/test.txt",FileMode.OpenOrCreate);       
10        Console.WriteLine("您可以嘗試在系統(tǒng)中刪除c盤下的test.txt(回車鍵繼續(xù))");
11        //暫停程序執(zhí)行﹐并嘗試在系統(tǒng)中刪除那個文件
12        Console.ReadLine();
13
14        //刪除文件測試
15        try
16        {
17            File.Delete(@"c:/test.txt");
18        }
19        catch (IOException ex)
20        {
21            Console.WriteLine("[Error]程序刪除文件失敗﹕{0}",ex.Message);
22        }
23    }
24}


3.在程序掛起時(Console.ReadLine等待輸入)﹐刪除文件會失敗﹐很容易理解﹐因為文件打開后沒有將其關(guān)閉﹐系統(tǒng)不知道這個文件是否還有用﹐所以幫我們保護(hù)這個文件(理所當(dāng)然﹐那個非托管資源所使用的內(nèi)存還被程序占用著)

4.但是在程序執(zhí)行完后﹐我們再嘗試刪除文件﹐成功﹗為什么?(fs不是沒有關(guān)閉那個SafeFileHandle嗎?)
當(dāng)然您可以說﹐windows操作系統(tǒng)在一個進(jìn)程結(jié)束后會自動回收其資源﹐沒錯(但是如果是com就慘了﹐因為com是存在于自己的獨立進(jìn)程內(nèi)﹐而操作系統(tǒng)不負(fù)責(zé)這個:(   )﹐不過這里不是因為windows操作系統(tǒng)的功能﹐而是.net垃圾收集器幫的忙。

5.看下面這個例子

 

 1using System;
 2using System.IO;
 3
 4public class TestFileStream
 5{
 6    public static void Main(string[] args)
 7    {
 8        //創(chuàng)建一個FileStream對象
 9        FileStream fs = new FileStream(@"C:/test.txt", FileMode.OpenOrCreate);
10        Console.WriteLine("您可以嘗試在系統(tǒng)中刪除c盤下的test.txt(回車鍵繼續(xù))");
11        //暫停程序執(zhí)行﹐并嘗試在系統(tǒng)中刪除那個文件
12        Console.ReadLine();
13
14        /**//*進(jìn)行垃圾收集*/
15        GC.Collect();
16        Console.WriteLine("再刪一下試試");
17        Console.ReadLine();
18    }
19}
 6.注意中間那行代碼:

GC.Collect();
這是強制要.net垃圾收集器進(jìn)行垃圾收集。
我們再去嘗試刪除test.txt﹐居然可以被刪除了﹐為什么呀?(fs不是沒有關(guān)閉那個SafeFileHandle嗎?)﹐讓我細(xì)細(xì)道來﹕

7.我們首先了解一下.net垃圾收集器進(jìn)行垃圾收集的四種時機(參見﹕.net框架程序設(shè)計 李建忠譯)
a.最常見的﹕當(dāng).net覺得合適時﹐例如它感到內(nèi)存緊張了(朮語稱為﹕0代對象充滿)
b.微軟強烈不建議使用的﹕GC的Collect方法調(diào)用(就是我們上面用的這種啦﹐因為會降低性能﹐會掛起進(jìn)程, 等等﹐反正聽微軟的吧。當(dāng)然某些時候可以用﹐就像我上面用來測試的代碼﹐呵呵...)
c.應(yīng)用程序域卸載時(AppDomain)
d.CLR被關(guān)閉時

8.現(xiàn)在我們可以明白第1個例子為什么在程序結(jié)束后文件可以被刪除﹐因為CLR被關(guān)閉時﹐.net執(zhí)行了垃圾收集(也就是等同于第二個例子的GC.Collect()代碼)

9.所以現(xiàn)在所有的問題都集中到垃圾收集上面﹐它做了什么?

a.垃圾收集器在判斷一個對象不會再被引用到后﹐就開始對它進(jìn)行垃圾收集(即回收內(nèi)存)
b.清空內(nèi)存(即把托管堆中的內(nèi)存收回來)
c.但是對象的有些字段引用到了非托管資源怎么辦?如FileStream的_handle
d.所以我們必須告訴垃圾收集器﹐在你回收我的內(nèi)存之前﹐先幫我執(zhí)行一個方法來收回我的非托管資源﹐以免托管堆的內(nèi)存被你回收了﹐而我引用的非托管資源的內(nèi)存卻被泄漏了。
e.這個方法就是Finalize()﹐也就是C#的 ~ClassName() 方法(同C++中的析構(gòu)語法)
f.所以一個對象如果存在Finalize方法時﹐垃圾收集器在收回它的內(nèi)存之前就會自動調(diào)用這個方法
g.這樣我們就可以把那些東東(非托管資源)給清理干凈了

由此看來﹐垃圾收集器提供的這種機制就是為了更好的完善.net的自動內(nèi)存管理的功能﹐讓我們也可以參與到垃圾收集中去

10.我們再來看看GC.Collect()這行代碼或CLR關(guān)閉時.Net做了什么﹕
a.垃圾收集器啟動﹐發(fā)現(xiàn)fs引用的那個對象已經(jīng)沒用了(當(dāng)然CLR關(guān)閉時才不管你有沒有用﹐通通回收)﹐于是對它進(jìn)行內(nèi)存回收
b.發(fā)現(xiàn)fs的類型﹕FileStream提供了Finalize方法﹐于是先調(diào)用這個方法
(以下通過Reflector繼續(xù))
c.Finalize方法中有 this._handle.Dispose()代碼﹐于是調(diào)用SafeHandler.Dispose()
d.接著轉(zhuǎn)到(當(dāng)然好多個圈﹐您悠著點...)SafeFileHandle.ReleaseHandle方法﹐發(fā)現(xiàn)代碼﹕Win32Native.CloseHandle() (即關(guān)閉非托管資源--文件HANDLE)

真相大白﹕原來是垃圾收集器幫我們關(guān)閉了那個非托管資源(當(dāng)然還是通過我們自己寫的Finalize方法)﹐因此后面就可以刪除文件了。

11.有人會問﹕好像我們平時在使用FileStream對象時﹐沒這么復(fù)雜呀?
答﹕Very Good!

一部分人﹕是因為大家都和我的例1一樣有好運氣﹐那個C盤下的test.txt文件自從被創(chuàng)建后﹐我壓根就不會再去用它﹐管它這部分資源有沒有被泄漏﹐有沒有被鎖定﹐最后程序結(jié)束時﹐被垃圾收集器幫了忙﹐把忘了關(guān)閉的文件HANDLE給收回來了。

剩下的一部分人﹕在程序里埋下了一顆"啞彈"﹐不知什么時候會爆炸﹐就像我例子中的File.Delete方法就出現(xiàn)了異常。

(不過我覺得)絕大多數(shù)人﹕是在看了很多諸如.net編程忠告﹐Microsoft強烈建議﹐MSDN標(biāo)準(zhǔn)做法等等等等( 還有我這篇blog﹐呵呵)之后﹐知道了在使用如FileStream,SqlConnection這些東東時﹐必須將其Close。

12.Close與Dispose
查看我們那兩個例子的代碼﹐都是不標(biāo)準(zhǔn)的﹐正確做法應(yīng)該在使用完那個FileStream后﹐調(diào)用fs.Close()將其關(guān)閉﹐以保證資源的安全。

附﹕正確做法


 

 1using System;
 2using System.IO;
 3
 4public class TestFileStream
 5{
 6    public static void Main(string[] args)
 7    {
 8        //創(chuàng)建一個FileStream對象
 9        FileStream fs = new FileStream(@"C:/test.txt", FileMode.OpenOrCreate);
10
11        /**//*在用完FileStream后關(guān)閉*/
12        fs.Close();
13
14        //刪除文件測試
15        try
16        {
17            File.Delete(@"c:/test.txt");
18        }
19        catch (IOException ex)
20        {
21            Console.WriteLine("[Error]程序刪除文件失敗﹕{0}", ex.Message);
22        }
23    }
24}
 

13.有人舉手﹐講這么多﹐早告訴我調(diào)用fs.Close不就得了。
哥們﹐fs.Close()方法是由您寫的﹐調(diào)不調(diào)用﹐手在您身上﹐您不調(diào)用的話﹐哪天程序出了問題﹐您有會叫﹕微軟真垃圾﹐.net真不穩(wěn)定﹐還是java好﹐安全﹐可靠...    為防您的國罵﹐MS只好在垃圾收集中加這一款﹐以防不測...

14.Dispose模式
認(rèn)真查看.net類庫中的那些基本類別﹐凡是有Finalize方法的類別﹐基本上都提供了諸如Dispose,Close,Dispose(bool)等方法(FileStream也不例外)

15.其實不管是Dispose,Close,Finalize方法﹐最終應(yīng)該都是執(zhí)行相同的代碼
區(qū)別﹕
Finalize方法﹕只能由微軟調(diào)用
Dispose和Close方法﹕提供給您調(diào)用
因此在您使用完那些類別后﹐那就直接調(diào)用Close吧(沒有Close﹐再調(diào)用Dispose方法)﹐當(dāng)然萬一您忘了﹐也別擔(dān)心﹐還有垃圾收集器幫您墊后。

七.第二個結(jié)論﹕
1.在您開發(fā)一個封裝非托管資源(即類中的字段引用到了非托管資源)的類別時﹕
A:強烈建議您提供Finalize方法進(jìn)行非托管資源的釋放﹐.net垃圾收集器不會幫您自動回收那部分資源﹐而是通過調(diào)用您的Finalize方法來幫您釋放。(這樣可以保證﹕在使用您類別的那位程序員忘了手動回收內(nèi)存時﹐還可通過垃圾收集器來補救)

B.強烈建議您提供一個Close或Dispose方法﹐以便使用您類別的程序員可以手動釋放您的類別中的非托管資源。(參見.net框架程序設(shè)計 自動內(nèi)存管理一章實現(xiàn)Dispose模式)

C.如果類別封裝了像FileStream這樣的對象(即對非托管資源的再次封裝)時﹐一般也應(yīng)該提供一 個Close或Dispose方法﹐除非您的這個成員保證在每次使用后﹐都被正常的關(guān)閉﹐即對調(diào)用者透明。

2.在您使用一個封裝非托管資源的類別時﹕
A:強烈建議您在明確知道這個類別沒有用之后﹐調(diào)用其提供的Close或Dispose方法手動釋放其非托管資源的 內(nèi)存。有道是﹕有借有還﹐再借不難;借了不還﹐再借休想~~

B:注意在手動釋放后﹐不要再調(diào)用該對象的相關(guān)方法了﹐因為對象已經(jīng)損毀了

再次BTW:不管是Finalize﹐Close還是Dispose﹐您都無法顯式釋放托管堆內(nèi)存﹐它們永遠(yuǎn)是微軟的"私人財產(chǎn) "﹕)
http://www.survivalescaperooms.com/tsoukw/archive/2006/12/08/586525.html


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 通海县| 麦盖提县| 正宁县| 麟游县| 绩溪县| 榕江县| 烟台市| 泊头市| 甘肃省| 乌兰察布市| 类乌齐县| 孟津县| 扎鲁特旗| 琼结县| 大厂| 淄博市| 尼勒克县| 泽州县| 综艺| 纳雍县| 卢氏县| 霍邱县| 舒兰市| 桦南县| 满城县| 宁晋县| 宜川县| 突泉县| 灌云县| 资源县| 西乌珠穆沁旗| 喀喇| 察哈| 奎屯市| 孝义市| 泗阳县| 新乐市| 日喀则市| 开封市| 延寿县| 高陵县|