




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第C#內(nèi)存管理CLR深入講解(下篇)《上篇》中我們主要討論的是程序集(Assembly)和應(yīng)用程序域(AppDomain)的話題,著重介紹了兩個不同的程序集加載方式獨占方式和共享方式(中立域方式);以及基于進程范圍內(nèi)的字符串駐留。這篇將關(guān)注點放在托管對象創(chuàng)建時內(nèi)存的分配和對大對象(LO:LargeObject)的回收上,不對之處,還望各位能夠及時指出。
一、從類型(Type)與實例(Instance)談起
在面向?qū)ο蟮氖澜缰?,類型和實例是兩個核心的要素。不論是類型和實例,相關(guān)的信息比如加載到內(nèi)存中,對應(yīng)著某一塊或者多塊連續(xù)或者不連續(xù)的內(nèi)存。那么對類型和實例的內(nèi)存分配時如何進行的呢?對象是狀態(tài)和行為的組合體,所以從.NETFramework的角度來看類型,它只具有兩種類型的成員字段和方法(實際還有嵌套類型),前者表示狀態(tài),后者表示行為。類型是對元數(shù)據(jù)的描述,而實例則是符合該元數(shù)據(jù)描述的單個個體。同一個類型下的所有實例具有相同的行為,它們通過狀態(tài)值的不同得以區(qū)分。所以內(nèi)存中的實例(本篇所說的實例指代引用類型的實例)表示的是字段值,而內(nèi)存中的類型表示的則是類型成員結(jié)構(gòu)的元數(shù)據(jù)。很多人都知道,當我們創(chuàng)建一個對象的時候,CLR會在GC堆(Heap)中開辟一塊連續(xù)的內(nèi)存空間保存字段值。那么類型信息又是保存在那塊內(nèi)存上呢?
實際上,類型信息保存在另一堆上,我們稱之為加載器堆(LoaderHeap)。每一個應(yīng)用程序域都具有各自的加載器堆,即包括我們創(chuàng)建的普通應(yīng)用程序域,也包括《上篇》中提到的三個特殊應(yīng)用程序域:系統(tǒng)程序域、共享程序域和默認程序域。如果說GC堆是實例的容器,那么基于應(yīng)用程序域的加載器堆就是類型的容器。CLR采用按需加載(這里指的是類型,不是程序集)、及時編譯的運行機制。當某個類型被第一次使用的時候,CLR試圖加載該類型。如果該類型對應(yīng)的程序沒有獨自地加載到本應(yīng)用程序域中,或者沒有通過中立域的形式加載到共享程序域中,它會按照相應(yīng)的方式加載程序集(在這里我們假設(shè)采用獨占方式加載)。然后,將使用到的這個類型加載到本應(yīng)用程序域的加載器堆中。
加載器堆維護著自應(yīng)用程序域創(chuàng)建以來使用過的所有類型記錄,它們對應(yīng)著一個特殊的對象方法表(MethodTable)。當程序第一次執(zhí)行到某個方法的時候,CLR會定位到方法表中該條目,獲取相關(guān)信息進行JIT編譯。所以如果某個類型在加載器堆中的方法表的某個條目至少被執(zhí)行一次,它就會指向一段JIT編譯后的機器指令。
二、實例內(nèi)存分配不僅限于GC堆
到現(xiàn)在為止,我們知道了類型和實例分別分配于基于應(yīng)用程序域的加載器堆和GC堆中,那么CLR的內(nèi)存分配僅僅限于這兩堆嗎?當然不是,除了這兩堆以及默認的進程堆,還有額外兩堆,一是存放JIT編譯后機器指令的JIT堆(JITHeap),另一個則是專門用于大對象的大對象堆(LOH:LargeObjectHeap)。下圖反映了CLR主要維護的這些個不同的堆。
對于大對象堆,在本文后續(xù)部分還會講述,在這里我們需要先了解CLR認為怎樣的對象是大對象。當我們實例化一個對象的時候,如果該對象大于或者等于85,000字節(jié)(這種對象一般是數(shù)組,一般對象不會這么大),CLR將認為是大對象并被放到LOH中,否則放到GC堆中。這里有一點需要讀者注意的是,作為垃圾回收器的GC并不僅僅限于針對GC堆中對象的回收,LOH中的對象的回收工作通過在GC的管轄之下。所以從某種意義上講:你可以將之前提到的GC堆理解為SOH(SmallObjectHeap),或者稱之為狹義GC堆,而將廣義GC堆理解為SOH+LOH。
三、實例對類型的引用
實例是類型的實例,實例和它所對應(yīng)的類型需要維持一種聯(lián)系。反映在內(nèi)存中,就以為著分配在GC堆或者是LOH中的對象具有一個對位于加載器堆中該類型的方法表的引用。實例對類型的引用通過一個特殊的對象來維系TypeHandle。我們舉個例子,在如下一段簡單的對象實例化代碼中,我先后實例化了四個對象:字符串ABC、System.Object對象、自定義Bar對象和具有85000個元素的字節(jié)數(shù)組。
stringstrInstance="ABC";
objectobjectInstance=newobject();
BarbarInstance=newBar()
byte[]largeObjInstance=newbyte[85000];
當上面的程序執(zhí)行后,圍繞著實例化的四個對象和類型信息,在內(nèi)存中將會具有如下一個關(guān)系。最左邊的是現(xiàn)成調(diào)用棧中的上述四個變量,對于字符串類型的strInstance,由于《上篇》所講述的關(guān)于字符串駐留機制,最后總的字符串被分配到系統(tǒng)程序域中;Object和Bar類型的objectInstance與barInstance由于是小于85000字節(jié)的小對象,所以被分配到GC堆中。objectInstance通過TypeHandle指向位于共享程序域中System.Objhect類型對應(yīng)的方法表(因為定義該類型的mscorlib程序集以中立域的方式加載),而barInstance得TypeHandle指向的基于Bar類型的方法表則位于默認程序域中(因為程序域默認采用獨占的方式加載)。元素個數(shù)為85000的字節(jié)數(shù)組largeObjInstance屬于大對象,直接分配到LOH中。largeObjInstance的TypeHandle指向的基于System.Byte[]類型的方法表,該System.Byte[]類型同樣定義在mscorlib程序集中,所以該方法表同樣存在于共享程序域的加載器堆。
四、LOH中的對象如何被回收
了解GC的讀者應(yīng)該都知道CLR采用基于代齡(Generation)的垃圾回收機制。代齡,個人覺得是一個很準確的詞語,它充分體現(xiàn)了設(shè)計者用于表現(xiàn)不同的對象具有不同生命周期的意思。所有對象分三代,即G0、G1和G2,這實際上代表了三個不同的連續(xù)的內(nèi)存塊。輩分越高,表明時間越久;輩分越低,被掃蕩(GC回收)的頻率就越高。關(guān)于基于代齡的垃圾回收機制,限于篇幅,就說到這里。我們的重點是GC采用怎樣的機制對LOH的對象進行回收。
到目前為止,對于LOH和GC堆中的對象,除了大小之外,我們好像沒有覺得它們之間有何不同。實際上,將大對象放在LOH中,目的在于對其實施特殊的回收機制。關(guān)于垃圾收回,我們應(yīng)該有這樣的認知:回收的成本是和對象的大小基本成正向關(guān)系,對象越大,回收成本就越大。所以我們不能對大對象頻繁地實施垃圾回收,實際上CLR是將LOH對象當成最高代齡的對象。也就是說,針對LOH的回收工作是和GC堆中G2一并進行的。換句話說,當G2或者LOH的剩余空間低于某個限度,針對它們的垃圾回收便被觸發(fā)。關(guān)于LOH的垃圾回收機制,我們可以通過一個非常簡單的程序來驗證。
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)建了兩個WeakReference對象,它們的Target分別被設(shè)置成byte[84000]和byte[85000]。按照我們上面關(guān)于對大
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 簽訂空白補償協(xié)議書
- 系統(tǒng)接入授權(quán)協(xié)議書
- 簽訂調(diào)解和解協(xié)議書
- 線路維護保養(yǎng)協(xié)議書
- 貸款財產(chǎn)贈予協(xié)議書
- 股東中介轉(zhuǎn)租協(xié)議書
- 老人房子處理協(xié)議書
- 美容公司轉(zhuǎn)讓協(xié)議書
- 2025年藥師考試藥物禁忌知識試題及答案
- 應(yīng)試技巧2025年執(zhí)業(yè)醫(yī)師考試試題及答案
- 職業(yè)技能鑒定指導書《變電站值班員(第二版)》高級工題庫(全)
- 2023年江蘇南京林業(yè)大學招聘工作人員10人筆試參考題庫(共500題)答案詳解版
- JGJT10-2011 混凝土泵送技術(shù)規(guī)程
- 人教版小學道德與法治二年級下冊第二單元《我們好好玩》作業(yè)設(shè)計
- 簡單裝飾裝修施工方案范本
- 電子基本電路安裝與測試考試復習題庫(含答案)
- 01S201室外消火栓安裝圖集
- 彩鋼板屋面監(jiān)理細則
- 文藝復興史學習通超星課后章節(jié)答案期末考試題庫2023年
- 《BIM技術(shù)概論》期末試卷及答案2套
- 受托支付合同
評論
0/150
提交評論