日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DotNET内存管理与垃圾回收[转]

發(fā)布時(shí)間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DotNET内存管理与垃圾回收[转] 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. Stack和Heap(堆)
??? 每個(gè)線程對應(yīng)一個(gè)stack,線程創(chuàng)建的時(shí)候CLR為其創(chuàng)建這個(gè)stack,stack主要作用是記錄函數(shù)的執(zhí)行情況。值類型變量(函數(shù)的參數(shù)、局部變量等非成員變量)都分配在stack中,引用類型的對象分配在heap中,在stack中保存heap對象的引用指針。GC只負(fù)責(zé)heap對象的釋放,heap內(nèi)存空間管理

Heap內(nèi)存分配
???
??? 除去pinned object等影響,heap中的內(nèi)存分配很簡單,一個(gè)指針記錄heap中分配的起始地址,根據(jù)對象大小連續(xù)的分配內(nèi)存

Stack結(jié)構(gòu)
??? 每個(gè)函數(shù)調(diào)用時(shí),邏輯上在thread stack中會產(chǎn)生一個(gè)幀(stack frame),函數(shù)返回時(shí)對應(yīng)的stack frame被釋放掉
??? 用個(gè)簡單的函數(shù)查看執(zhí)行時(shí)CLR對棧的處理情況:
static?void?Main(string[]?args)
{
????
int?r?=?Sum(2,?3,?4,?5,?6);
}
private?static?int?Sum(int?a,?int?b,?int?c,?int?d,?int?e)
{
????
return?a?+?b?+?c?+?d?+?e;
}
??? JIT編譯后主要匯編代碼如下(其他的情況下匯編代碼可能有所差別,但用這個(gè)簡單函數(shù)大致看下棧的管理已經(jīng)足夠):
;====函數(shù)Main====
push????4?????????;第3個(gè)參數(shù)到最后一個(gè)參數(shù)壓棧
push????5????
push????6????
mov????edx,3???;第1、第2個(gè)參數(shù)分別放入ecx、edx寄存器
mov????ecx,2?
call????dword?ptr?ds:[00AD96B8h]??;調(diào)用函數(shù)Sum,執(zhí)行call的時(shí)候返回地址(即下面這條mov語句的地址)自動壓棧了
mov????dword?ptr?[ebp-0Ch],eax???;將函數(shù)返回值設(shè)置到局部變量r中(函數(shù)調(diào)用結(jié)束返回值在eax寄存器中)

;
====函數(shù)Sum====
push????ebp???????????;保存原始ebp寄存器
mov????ebp,esp?????;將當(dāng)前棧指針保存在ebp中,后面使用ebp對參數(shù)和局部變量尋址
sub????esp,8?????????;分配兩個(gè)局部變量
mov????dword?ptr?[ebp-4],ecx?????????;第1個(gè)參數(shù)放入局部變量
mov????dword?ptr?[ebp-8],edx?????????;第2個(gè)參數(shù)放入局部變量
......???? ;CLR的檢查代碼
mov????eax,dword?ptr?[ebp-4]??????????;a?+?b?+?c?+?d?+?e
add????eax,dword?ptr?[ebp-8]??????????;第1個(gè)參數(shù)+第2個(gè)參數(shù)(2+3)
add????eax,dword?ptr?[ebp+10h]??????;+第3個(gè)參數(shù)(4)
add????eax,dword?ptr?[ebp+0Ch]??????;+第4個(gè)參數(shù)(5)
add????eax,dword?ptr?[ebp+8]??????????;+第5個(gè)參數(shù)(6)
mov????esp,ebp????;恢復(fù)棧指針(局部變量被釋放了)
pop????ebp??????????;恢復(fù)原始的ebp寄存器值
ret????0Ch???;函數(shù)返回.?1:?返回地址自動出棧;?2:?esp減去0Ch(12個(gè)字節(jié)),即從棧中清除調(diào)用參數(shù);?3:?返回值在eax寄存器中??? 執(zhí)行時(shí)刻的stack狀態(tài)如下(?;刂窞楦叨说刂?#xff0c;棧頂為低端地址):
???
??? Stack狀態(tài)變化過程:
??? a). 調(diào)用者將第3、第4、第5個(gè)參數(shù)壓棧,第1、第2個(gè)參數(shù)分別放入ecx、edx寄存器
??? b). call指令調(diào)用函數(shù)Sum,并自動將函數(shù)返回地址壓棧,代碼跳轉(zhuǎn)到函數(shù)Sum開始執(zhí)行
??? c). 函數(shù)Sum先將寄存器ebp壓棧保存,并將esp放入ebp,用于后面對參數(shù)和局部變量尋址
??? d). 定義局部變量以及省略掉的是額外代碼,跟Sum函數(shù)業(yè)務(wù)無關(guān)
??? e). 執(zhí)行加法操作,結(jié)果保存在eax寄存器中
??? f). 恢復(fù)esp寄存器,這樣函數(shù)Sum中所有的局部變量以及其他壓棧操作全部釋放出來
??? g). 原始ebp的值出棧,恢復(fù)ebp,這樣棧完全恢復(fù)到進(jìn)入Sum函數(shù)調(diào)用時(shí)的狀態(tài)
??? h). ret指令執(zhí)行函數(shù)返回,返回值在eax寄存器中,返回地址為call指令壓棧的地址,返回地址自動出棧。0Ch指示處理器在函數(shù)返回時(shí)釋放棧中12個(gè)字節(jié),即由被調(diào)用者清除壓棧的參數(shù)。函數(shù)返回之后,本次Sum調(diào)用的棧分配全部釋放
??? 這種調(diào)用約定類似__fastcall

??? 結(jié)合引用類型變量、值類型的ref參數(shù),下面代碼簡化的stack狀態(tài)如下:
??? 代碼:
public?static?void?Run(int?i)
{
????
int?j?=?9;
????MyClass1?c?
=?new?MyClass1();
????c.x?
=?8;
????
int?result?=?Sum(i,?5,?ref?j,?c);
}

public?static?int?Sum(int?a,?int?b,?ref?int?c,?MyClass1?obj)
{
????
int?r?=?a?+?b?+?c?+?obj.x;
????
return?r;
}

public?class?MyClass1
{
????
public?int?x;
}
??? Stack狀態(tài):
???
??? 任何時(shí)候引用類型都分配在heap中,在stack中只是保存對象的引用地址。Run函數(shù)執(zhí)行完畢之后,heap中的MyClass1對象c成為可回收的垃圾對象,在GC時(shí)進(jìn)行回收

2. Mark-Compact 標(biāo)記壓縮算法
??? 簡單把.NET的GC算法看作Mark-Compact算法
??? 階段1: Mark-Sweep 標(biāo)記清除階段
??? 先假設(shè)heap中所有對象都可以回收,然后找出不能回收的對象,給這些對象打上標(biāo)記,最后heap中沒有打標(biāo)記的對象都是可以被回收的
??? 階段2: Compact 壓縮階段
??? 對象回收之后heap內(nèi)存空間變得不連續(xù),在heap中移動這些對象,使他們重新從heap基地址開始連續(xù)排列,類似于磁盤空間的碎片整理
???
??? Heap內(nèi)存經(jīng)過回收、壓縮之后,可以繼續(xù)采用前面的heap內(nèi)存分配方法,即僅用一個(gè)指針記錄heap分配的起始地址就可以

??? 主要處理步驟:將線程掛起=>確定roots=>創(chuàng)建reachable objects graph=>對象回收=>heap壓縮=>指針修復(fù)
??? 可以這樣理解roots:heap中對象的引用關(guān)系錯(cuò)綜復(fù)雜(交叉引用、循環(huán)引用),形成復(fù)雜的graph,roots是CLR在heap之外可以找到的各種入口點(diǎn)。GC搜索roots的地方包括全局對象、靜態(tài)變量、局部對象、函數(shù)調(diào)用參數(shù)、當(dāng)前CPU寄存器中的對象指針(還有finalization queue)等。主要可以歸為2種類型:已經(jīng)初始化了的靜態(tài)變量、線程仍在使用的對象(stack+CPU register)
??? Reachable objects:指根據(jù)對象引用關(guān)系,從roots出發(fā)可以到達(dá)的對象。例如當(dāng)前執(zhí)行函數(shù)的局部變量對象A是一個(gè)root object,他的成員變量引用了對象B,則B是一個(gè)reachable object。從roots出發(fā)可以創(chuàng)建reachable objects graph,剩余對象即為unreachable,可以被回收
???
??? 指針修復(fù)是因?yàn)閏ompact過程移動了heap對象,對象地址發(fā)生變化,需要修復(fù)所有引用指針,包括stack、CPU register中的指針以及heap中其他對象的引用指針
??? Debug和release執(zhí)行模式之間稍有區(qū)別,release模式下后續(xù)代碼沒有引用的對象是unreachable的,而debug模式下需要等到當(dāng)前函數(shù)執(zhí)行完畢,這些對象才會成為unreachable,目的是為了調(diào)試時(shí)跟蹤局部對象的內(nèi)容
??? 傳給了COM+的托管對象也會成為root,并且具有一個(gè)引用計(jì)數(shù)器以兼容COM+的內(nèi)存管理機(jī)制,引用計(jì)數(shù)器為0時(shí)這些對象才可能成為被回收對象
??? Pinned objects指分配之后不能移動位置的對象,例如傳遞給非托管代碼的對象(或者使用了fixed關(guān)鍵字),GC在指針修復(fù)時(shí)無法修改非托管代碼中的引用指針,因此將這些對象移動將發(fā)生異常。pinned objects會導(dǎo)致heap出現(xiàn)碎片,但大部分情況來說傳給非托管代碼的對象應(yīng)當(dāng)在GC時(shí)能夠被回收掉

3. Generational 分代算法
??? 程序可能使用幾百M(fèi)、幾G的內(nèi)存,對這樣的內(nèi)存區(qū)域進(jìn)行GC操作成本很高,分代算法具備一定統(tǒng)計(jì)學(xué)基礎(chǔ),對GC的性能改善效果比較明顯
??? 將對象按照生命周期分成新的、老的,根據(jù)統(tǒng)計(jì)分布規(guī)律所反映的結(jié)果,可以對新、老區(qū)域采用不同的回收策略和算法,加強(qiáng)對新區(qū)域的回收處理力度,爭取在較短時(shí)間間隔、較小的內(nèi)存區(qū)域內(nèi),以較低成本將執(zhí)行路徑上大量新近拋棄不再使用的局部對象及時(shí)回收掉
??? 分代算法的假設(shè)前提條件:
??? a). 大量新創(chuàng)建的對象生命周期都比較短,而較老的對象生命周期會更長
??? b). 對部分內(nèi)存進(jìn)行回收比基于全部內(nèi)存的回收操作要快
??? c). 新創(chuàng)建的對象之間關(guān)聯(lián)程度通常較強(qiáng)。heap分配的對象是連續(xù)的,關(guān)聯(lián)度較強(qiáng)有利于提高CPU cache的命中率

??? .NET將heap分成3個(gè)代齡區(qū)域: Gen 0、Gen 1、Gen 2
???
??? Heap分為3個(gè)代齡區(qū)域,相應(yīng)的GC有3種方式: # Gen 0 collections, # Gen 1 collections, # Gen 2 collections。如果Gen 0 heap內(nèi)存達(dá)到閥值,則觸發(fā)0代GC,0代GC后Gen 0中幸存的對象進(jìn)入Gen 1。如果Gen 1的內(nèi)存達(dá)到閥值,則進(jìn)行1代GC,1代GC將Gen 0 heap和Gen 1 heap一起進(jìn)行回收,幸存的對象進(jìn)入Gen 2。2代GC將Gen 0 heap、Gen 1 heap和Gen 2 heap一起回收
??? Gen 0和Gen 1比較小,這兩個(gè)代齡加起來總是保持在16M左右;Gen 2的大小由應(yīng)用程序確定,可能達(dá)到幾G,因此0代和1代GC的成本非常低,2代GC稱為full GC,通常成本很高。粗略的計(jì)算0代和1代GC應(yīng)當(dāng)能在幾毫秒到幾十毫秒之間完成,Gen 2 heap比較大時(shí)full GC可能需要花費(fèi)幾秒時(shí)間。大致上來講.NET應(yīng)用運(yùn)行期間2代、1代和0代GC的頻率應(yīng)當(dāng)大致為1:10:100
???
??? 圖為一個(gè)ASP.NET程序運(yùn)行的Performance Moniter,Gen 0 heap size(紅色)平均6M,Gen 1(藍(lán)色)平均5M,Gen 2(黃色)達(dá)到620M,Gen 0+Gen 1平均13.2M,最大19.8M

??? 直觀上來看,程序的運(yùn)行由一系列函數(shù)調(diào)用組成,函數(shù)運(yùn)行期間會創(chuàng)建很多局部對象,函數(shù)結(jié)束之后也就產(chǎn)生大量待回收的對象。采用分代算法加強(qiáng)較新代齡的垃圾回收力度,通常能夠極大的提高垃圾回收效率,否則就是極特殊的程序,或者是不合理的對象關(guān)聯(lián)設(shè)計(jì)。例如ASP.NET程序,應(yīng)當(dāng)確保絕大部分用于HTTP 請求處理的對象在0代和1代垃圾回收中被釋放掉

??? 為heap記錄幾個(gè)指針可以確定代齡區(qū)域范圍,創(chuàng)建reachable objects graph時(shí)根據(jù)對象的地址可以確定對象位于哪個(gè)代齡區(qū)域,0代GC在創(chuàng)建graph時(shí)如果遇到1代、2代heap對象,可以直接越過不用繼續(xù)遍歷下去,較老代齡的對象如果引用了較新代齡的對象,可以通過Win32 API GetWriteWatch訂閱內(nèi)存更新通知,記錄在"card table"中,輔助較低代齡的GC正確構(gòu)造graph

4. LOH
??? .NET 1.1和2.0中,85000字節(jié)以下的對象稱為小對象,分配在Gen 0 heap中,85000字節(jié)以上的對象稱為大對象,分配在Large Object Heap中,這是因?yàn)镚C在heap壓縮時(shí)移動大的內(nèi)存塊需要消耗大量CPU時(shí)間,通過性能調(diào)優(yōu)實(shí)踐確定了85000字節(jié)這樣一個(gè)閥值
??? LOH只在2代GC時(shí)進(jìn)行回收,采用Mark-Sweep算法,沒有壓縮處理,因此LOH中的內(nèi)存分配是不連續(xù)的,使用一個(gè)空閑列表free list記錄LOH中的空閑空間,對釋放出來的空間進(jìn)行管理
???
??? 上圖中obj1、obj2釋放之后,其空間合并起來成為free list的一個(gè)節(jié)點(diǎn),隨后被分配給obj4

??? 什么時(shí)候觸發(fā)垃圾回收?
??? 前面已經(jīng)提到,0代和1代垃圾回收主要由閥值控制。初始時(shí)Gen 0 heap大小與CPU緩存的大小相關(guān),運(yùn)行時(shí)CLR根據(jù)內(nèi)存請求狀態(tài)動態(tài)調(diào)整Gen 0 heap大小,但Gen 0和Gen 1總大小保持在16M左右
Gen 2 heap和LOH都在full GC時(shí)進(jìn)行回收,full GC主要由2類事件觸發(fā):
??? a). 進(jìn)入Gen 2 heap和LOH的對象很多,超過了一定比例。RegisterForFullGCNotification的參數(shù) maxGenerationThreshold、largeObjectHeapThreshold可以分別為Gen 2 heap和LOH設(shè)定這個(gè)值
??? b). 操作系統(tǒng)內(nèi)存吃緊的時(shí)候。CLR會接收到操作系統(tǒng)內(nèi)存緊張的通知消息,觸發(fā)full GC

5. Heap細(xì)節(jié)、擴(kuò)容與收縮
??? Heap的代齡是邏輯上的結(jié)構(gòu),heap實(shí)際內(nèi)存申請和分配以及釋放以segment(段)為單位,workstation GC模式segment大小為16M,server GC模式segment大小為64M。Gen 0和Gen 1 heap總是位于同一個(gè)段中,叫做ephemeral segment(新生段),因此max(Gen 0 heap size+Gen 1 heap size)≈16M || 64M,Gen 2 heap由0個(gè)或多個(gè)segments組成,LOH由1個(gè)或多個(gè)segments組成
??? .NET程序啟動時(shí)CLR為heap創(chuàng)建2個(gè)segment,一個(gè)作為ephemeral segment,另一個(gè)用于LOH。.NET使用VirtualAlloc申請和分配heap內(nèi)存,在LOH中分配新對象時(shí)沒有足夠的空間,或者1代GC 時(shí)進(jìn)入Gen 2的對象過多空間不夠,.NET將為LOH或者小對象heap分配新的segment。申請新的segment失敗將由EE拋出OutOfMemory異常
??? Full GC后完全空閑的segments將被釋放掉,內(nèi)存返回給操作系統(tǒng)

??? .NET 2.0對GC的一個(gè)重要改進(jìn)是盡量改善heap碎片處理。heap碎片主要由pinned objects引起,改善措施主要有2個(gè)方面。首先是延遲升級,如果ephemeral segment存在pinned objects,則盡可能的延遲他們升級到Gen 2的時(shí)間點(diǎn),考慮pinned objects的同時(shí)盡量充分利用當(dāng)前ephemeral segment的空間;其次是重復(fù)利用Gen 2的空間,如果Gen 2中存在pinned objects的segments釋放出了足夠空間,該segments可能重新作為ephemeral segment使用

6. GC方式
??? 有Workstation GC with Concurrent GC off、 Workstation GC with Concurrent GC on、Server GC 3種
??? Workstation GC with Concurrent GC off: 用于單CPU機(jī)器實(shí)現(xiàn)高吞吐量,采用一系列策略觀察內(nèi)存分配以及每次GC的狀況,動態(tài)調(diào)整GC策略,盡可能使程序隨著運(yùn)行時(shí)狀態(tài)的變化實(shí)現(xiàn)高效的GC操作,但進(jìn)行GC時(shí)會凍結(jié)所有線程
??? Workstation GC with Concurrent GC on: 用于響應(yīng)時(shí)間非常重要的交互式程序,例如流媒體的播放等(如果一次full GC導(dǎo)致應(yīng)用程序中斷幾秒、十幾秒時(shí)間,用戶將無法忍受)。這種方式利用多CPU對full GC進(jìn)行并行處理,不是整個(gè)full GC期間凍結(jié)所有線程,而是將full GC切分成多次很短的時(shí)間對線程進(jìn)行凍結(jié),在線程凍結(jié)時(shí)間之外,應(yīng)用程序仍然可以正常運(yùn)行,進(jìn)行內(nèi)存分配,這主要通過將Gen 0 heap size設(shè)置的比non-concurrent GC大很多而實(shí)現(xiàn),使得GC操作時(shí)線程仍然能夠在Gen 0 heap中進(jìn)行內(nèi)存分配,但如果Gen 0 heap用完后GC仍然沒有結(jié)束,線程仍然會出現(xiàn)阻塞。這種方式付出的代價(jià)是working set和GC所需時(shí)間比non-concurrent GC要大一些
??? Server GC: 用于多CPU機(jī)器的服務(wù)器應(yīng)用程序?qū)崿F(xiàn)高吞吐量和伸縮性,充分利用服務(wù)器的大內(nèi)存。.NET為每個(gè)CPU創(chuàng)建一組heap(包括Gen 0, 1, 2和LOH)和一個(gè)GC線程,每個(gè)CPU可以獨(dú)立的為相應(yīng)的heap執(zhí)行GC操作,而其他CPU則正常執(zhí)行處理。最佳的應(yīng)用場景是多線程之間內(nèi)存結(jié)構(gòu)基本相同,執(zhí)行的工作相同或類似

??? 單CPU機(jī)器上只能使用workstation GC,默認(rèn)情況下為Workstation GC with Concurrent GC on方式,單CPU機(jī)器上配置為Server GC無效,仍然使用workstation GC;多CPU服務(wù)器上的ASP.NET默認(rèn)使用Server GC方式,Server GC時(shí)不能使用concurrent方式
??? concurrent GC可以用于單CPU機(jī)器,它與CPU數(shù)量無關(guān)
??? 對于ASP.NET程序應(yīng)當(dāng)盡量保證一個(gè)CPU僅對應(yīng)一個(gè)GC線程,防止同一個(gè)CPU上面多個(gè)GC線程之間的沖突造成性能問題。如果使用了Web Garden則應(yīng)當(dāng)使用Workstation GC with Concurrent GC off。Web Garden為了提高吞吐量會導(dǎo)致多出幾倍的內(nèi)存使用,每個(gè)work process的內(nèi)存有很多重復(fù)部分,Web Garden的最佳應(yīng)用場景是多個(gè)進(jìn)程之間使用一個(gè)共享的resource pool,避免內(nèi)存的重復(fù)并盡可能的提高吞吐量。在這一點(diǎn)上Server GC應(yīng)當(dāng)與Web Garden類似,但Web Garden在多個(gè)進(jìn)程中,而Server GC是在同一個(gè)進(jìn)程中通過多線程實(shí)現(xiàn),目前沒有發(fā)現(xiàn)Server GC方面深入一些的資料,很多東西只能根據(jù)現(xiàn)有資料做一些猜想
??? 為workstation GC禁用concurrent GC:
<configuration>
????
<runtime>
????????
<gcConcurrent?enabled="false"/>
????
</runtime>
</configuration>
??? 啟用Server GC:
<configuration>
????
<runtime>
????????
<gcServer?enabled=“true"/>
????
</runtime>
</configuration>
7. Finalization
......

參考:
Garbage Collection - Past, Present and Future, Patrick Dussud, 中文翻譯: .NET垃圾收集器的過去現(xiàn)在和未來(一), (二)
C# Heap(ing) Vs Stack(ing) in .NET Part I, Part II, Part III, Part IV Matthew Cochran
Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework Jeffrey Richter
Garbage Collection Part 2: Automatic Memory Management in the Microsoft .NET Framework Jeffrey Richter
CLR Inside Out: Large Object Heap Uncovered Maoni Stephens
Heap: Pleasures and Pains Murali R. Krishnan
The Dangers of the Large Object Heap Andrew Hunter
Garbage Collection Notifications
Garbage Collector Basics and Performance Hints Rico Mariani
CLR Inside Out: Investigating Memory Issues Claudio Caldato and Maoni Stephens
Understanding Garbage Collection in .NET Andrew Hunter
Using GC Efficiently Part 1, Part 2, Part 3, Part 4 Maoni Stephens
Notes on the CLR Garbage Collector Vineet Gupta
The Mystery of Concurrent GC Mark Smith
Garbage Collection Curriculum Ferreira Paulo, Veiga Luís
Java theory and practice: A brief history of garbage collection Brian Goetz


來源:http://www.cnblogs.com/RicCC/archive/2009/09/01/dotnet-memory-management-and-garbage-collection.html

轉(zhuǎn)載于:https://www.cnblogs.com/guangrou/archive/2009/10/13/1582348.html

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的DotNET内存管理与垃圾回收[转]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。