C#內(nèi)存管理CLR深入講解(下篇)_第1頁
C#內(nèi)存管理CLR深入講解(下篇)_第2頁
C#內(nèi)存管理CLR深入講解(下篇)_第3頁
C#內(nèi)存管理CLR深入講解(下篇)_第4頁
C#內(nèi)存管理CLR深入講解(下篇)_第5頁
已閱讀5頁,還剩1頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第C#內(nèi)存管理CLR深入講解(下篇)《上篇》中我們主要討論的是程序集(Assembly)和應(yīng)用程序域(AppDomain)的話題,著重介紹了兩個(gè)不同的程序集加載方式獨(dú)占方式和共享方式(中立域方式);以及基于進(jìn)程范圍內(nèi)的字符串駐留。這篇將關(guān)注點(diǎn)放在托管對(duì)象創(chuàng)建時(shí)內(nèi)存的分配和對(duì)大對(duì)象(LO:LargeObject)的回收上,不對(duì)之處,還望各位能夠及時(shí)指出。

一、從類型(Type)與實(shí)例(Instance)談起

在面向?qū)ο蟮氖澜缰校愋秃蛯?shí)例是兩個(gè)核心的要素。不論是類型和實(shí)例,相關(guān)的信息比如加載到內(nèi)存中,對(duì)應(yīng)著某一塊或者多塊連續(xù)或者不連續(xù)的內(nèi)存。那么對(duì)類型和實(shí)例的內(nèi)存分配時(shí)如何進(jìn)行的呢?對(duì)象是狀態(tài)和行為的組合體,所以從.NETFramework的角度來看類型,它只具有兩種類型的成員字段和方法(實(shí)際還有嵌套類型),前者表示狀態(tài),后者表示行為。類型是對(duì)元數(shù)據(jù)的描述,而實(shí)例則是符合該元數(shù)據(jù)描述的單個(gè)個(gè)體。同一個(gè)類型下的所有實(shí)例具有相同的行為,它們通過狀態(tài)值的不同得以區(qū)分。所以內(nèi)存中的實(shí)例(本篇所說的實(shí)例指代引用類型的實(shí)例)表示的是字段值,而內(nèi)存中的類型表示的則是類型成員結(jié)構(gòu)的元數(shù)據(jù)。很多人都知道,當(dāng)我們創(chuàng)建一個(gè)對(duì)象的時(shí)候,CLR會(huì)在GC堆(Heap)中開辟一塊連續(xù)的內(nèi)存空間保存字段值。那么類型信息又是保存在那塊內(nèi)存上呢?

實(shí)際上,類型信息保存在另一堆上,我們稱之為加載器堆(LoaderHeap)。每一個(gè)應(yīng)用程序域都具有各自的加載器堆,即包括我們創(chuàng)建的普通應(yīng)用程序域,也包括《上篇》中提到的三個(gè)特殊應(yīng)用程序域:系統(tǒng)程序域、共享程序域和默認(rèn)程序域。如果說GC堆是實(shí)例的容器,那么基于應(yīng)用程序域的加載器堆就是類型的容器。CLR采用按需加載(這里指的是類型,不是程序集)、及時(shí)編譯的運(yùn)行機(jī)制。當(dāng)某個(gè)類型被第一次使用的時(shí)候,CLR試圖加載該類型。如果該類型對(duì)應(yīng)的程序沒有獨(dú)自地加載到本應(yīng)用程序域中,或者沒有通過中立域的形式加載到共享程序域中,它會(huì)按照相應(yīng)的方式加載程序集(在這里我們假設(shè)采用獨(dú)占方式加載)。然后,將使用到的這個(gè)類型加載到本應(yīng)用程序域的加載器堆中。

加載器堆維護(hù)著自應(yīng)用程序域創(chuàng)建以來使用過的所有類型記錄,它們對(duì)應(yīng)著一個(gè)特殊的對(duì)象方法表(MethodTable)。當(dāng)程序第一次執(zhí)行到某個(gè)方法的時(shí)候,CLR會(huì)定位到方法表中該條目,獲取相關(guān)信息進(jìn)行JIT編譯。所以如果某個(gè)類型在加載器堆中的方法表的某個(gè)條目至少被執(zhí)行一次,它就會(huì)指向一段JIT編譯后的機(jī)器指令。

二、實(shí)例內(nèi)存分配不僅限于GC堆

到現(xiàn)在為止,我們知道了類型和實(shí)例分別分配于基于應(yīng)用程序域的加載器堆和GC堆中,那么CLR的內(nèi)存分配僅僅限于這兩堆嗎?當(dāng)然不是,除了這兩堆以及默認(rèn)的進(jìn)程堆,還有額外兩堆,一是存放JIT編譯后機(jī)器指令的JIT堆(JITHeap),另一個(gè)則是專門用于大對(duì)象的大對(duì)象堆(LOH:LargeObjectHeap)。下圖反映了CLR主要維護(hù)的這些個(gè)不同的堆。

對(duì)于大對(duì)象堆,在本文后續(xù)部分還會(huì)講述,在這里我們需要先了解CLR認(rèn)為怎樣的對(duì)象是大對(duì)象。當(dāng)我們實(shí)例化一個(gè)對(duì)象的時(shí)候,如果該對(duì)象大于或者等于85,000字節(jié)(這種對(duì)象一般是數(shù)組,一般對(duì)象不會(huì)這么大),CLR將認(rèn)為是大對(duì)象并被放到LOH中,否則放到GC堆中。這里有一點(diǎn)需要讀者注意的是,作為垃圾回收器的GC并不僅僅限于針對(duì)GC堆中對(duì)象的回收,LOH中的對(duì)象的回收工作通過在GC的管轄之下。所以從某種意義上講:你可以將之前提到的GC堆理解為SOH(SmallObjectHeap),或者稱之為狹義GC堆,而將廣義GC堆理解為SOH+LOH。

三、實(shí)例對(duì)類型的引用

實(shí)例是類型的實(shí)例,實(shí)例和它所對(duì)應(yīng)的類型需要維持一種聯(lián)系。反映在內(nèi)存中,就以為著分配在GC堆或者是LOH中的對(duì)象具有一個(gè)對(duì)位于加載器堆中該類型的方法表的引用。實(shí)例對(duì)類型的引用通過一個(gè)特殊的對(duì)象來維系TypeHandle。我們舉個(gè)例子,在如下一段簡單的對(duì)象實(shí)例化代碼中,我先后實(shí)例化了四個(gè)對(duì)象:字符串ABC、System.Object對(duì)象、自定義Bar對(duì)象和具有85000個(gè)元素的字節(jié)數(shù)組。

stringstrInstance="ABC";

objectobjectInstance=newobject();

BarbarInstance=newBar()

byte[]largeObjInstance=newbyte[85000];

當(dāng)上面的程序執(zhí)行后,圍繞著實(shí)例化的四個(gè)對(duì)象和類型信息,在內(nèi)存中將會(huì)具有如下一個(gè)關(guān)系。最左邊的是現(xiàn)成調(diào)用棧中的上述四個(gè)變量,對(duì)于字符串類型的strInstance,由于《上篇》所講述的關(guān)于字符串駐留機(jī)制,最后總的字符串被分配到系統(tǒng)程序域中;Object和Bar類型的objectInstance與barInstance由于是小于85000字節(jié)的小對(duì)象,所以被分配到GC堆中。objectInstance通過TypeHandle指向位于共享程序域中System.Objhect類型對(duì)應(yīng)的方法表(因?yàn)槎x該類型的mscorlib程序集以中立域的方式加載),而barInstance得TypeHandle指向的基于Bar類型的方法表則位于默認(rèn)程序域中(因?yàn)槌绦蛴蚰J(rèn)采用獨(dú)占的方式加載)。元素個(gè)數(shù)為85000的字節(jié)數(shù)組largeObjInstance屬于大對(duì)象,直接分配到LOH中。largeObjInstance的TypeHandle指向的基于System.Byte[]類型的方法表,該System.Byte[]類型同樣定義在mscorlib程序集中,所以該方法表同樣存在于共享程序域的加載器堆。

四、LOH中的對(duì)象如何被回收

了解GC的讀者應(yīng)該都知道CLR采用基于代齡(Generation)的垃圾回收機(jī)制。代齡,個(gè)人覺得是一個(gè)很準(zhǔn)確的詞語,它充分體現(xiàn)了設(shè)計(jì)者用于表現(xiàn)不同的對(duì)象具有不同生命周期的意思。所有對(duì)象分三代,即G0、G1和G2,這實(shí)際上代表了三個(gè)不同的連續(xù)的內(nèi)存塊。輩分越高,表明時(shí)間越久;輩分越低,被掃蕩(GC回收)的頻率就越高。關(guān)于基于代齡的垃圾回收機(jī)制,限于篇幅,就說到這里。我們的重點(diǎn)是GC采用怎樣的機(jī)制對(duì)LOH的對(duì)象進(jìn)行回收。

到目前為止,對(duì)于LOH和GC堆中的對(duì)象,除了大小之外,我們好像沒有覺得它們之間有何不同。實(shí)際上,將大對(duì)象放在LOH中,目的在于對(duì)其實(shí)施特殊的回收機(jī)制。關(guān)于垃圾收回,我們應(yīng)該有這樣的認(rèn)知:回收的成本是和對(duì)象的大小基本成正向關(guān)系,對(duì)象越大,回收成本就越大。所以我們不能對(duì)大對(duì)象頻繁地實(shí)施垃圾回收,實(shí)際上CLR是將LOH對(duì)象當(dāng)成最高代齡的對(duì)象。也就是說,針對(duì)LOH的回收工作是和GC堆中G2一并進(jìn)行的。換句話說,當(dāng)G2或者LOH的剩余空間低于某個(gè)限度,針對(duì)它們的垃圾回收便被觸發(fā)。關(guān)于LOH的垃圾回收機(jī)制,我們可以通過一個(gè)非常簡單的程序來驗(yàn)證。

classProgram

staticWeakReferenceSmallObjRef;

staticWeakReferenceLargeObjRef;

staticvoidMain(string[]args)

SetValues();

GC.Collect(0);

Console.WriteLine("GC.Collect(0)");

Console.WriteLine("SmallObjRef.Target==null{0}",SmallObjRef.Target==null);

Console.WriteLine("LargeObjRef.Target==null{0}\n",LargeObjRef.Target==null);

GC.Collect(1);

Console.WriteLine("GC.Collect(1)");

Console.WriteLine("LargeObjRef.Target==null{0}\n",LargeObjRef.Target==null);

GC.Collect(2);

Console.WriteLine("GC.Collect(2)");

Console.WriteLine("LargeObjRef.Target==null{0}\n",LargeObjRef.Target==null);

staticvoidSetValues()

SmallObjRef=newWeakReference(newbyte[84000]);

LargeObjRef=newWeakReference(newbyte[85000]);

}

輸出結(jié)果:

GC.Collect(0)

SmallObjRef.Target==nullTrue

LargeObjRef.Target==nullFalse

GC.Collect(1)

LargeObjRef.Target==nullFalse

GC.Collect(2)

LargeObjRef.Target==nullTrue

在上面的代碼中沒,我創(chuàng)建了兩個(gè)WeakReference對(duì)象,它們的Target分別被設(shè)置成byte[84000]和byte[85000]。按照我們上面關(guān)于對(duì)大

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論