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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

CoreCLR文档翻译 - GC的设计

發(fā)布時間:2023/12/4 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CoreCLR文档翻译 - GC的设计 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

此文檔來源于CoreCLR的BOTR(The Book of the Runtime),?點擊打開原文
一切著作權歸微軟公司所有

GC的設計

作者: Maoni Stephens (@maoni0) - 2015

提示: 推薦看?The Garbage Collection Handbook?這本書學習更多關于GC的知識 (在文章底部的鏈接中)

組件結(jié)構(gòu)

在GC中有兩個主要的組件, 一個是分配器(Allocator), 另一個是收集器(Collector).
分配器負責獲取更多的內(nèi)存并且在適當?shù)臅r機觸發(fā)收集器.
收集器負責回收垃圾和不再被程序使用的對象內(nèi)存.

此外還有一些途徑可以觸發(fā)收集器, 例如手動調(diào)用GC.Collect函數(shù)或析構(gòu)線程(Finalizer Thread)收到一個內(nèi)存不足的異步通知(由收集器發(fā)送).

分配器的設計

分配器由運行引擎(Execution Engine (EE))調(diào)用, 調(diào)用時會帶有以下的信息:

  • 需要分配的大小

  • 線程專用的分配上下文(Allocation Context)

  • 標記, 如對象是否有析構(gòu)函數(shù)

GC不會根據(jù)對象類型的不同做出特殊處理, 它會從運行引擎獲取對象的大小, 根據(jù)對象的大小把對象分為兩類:

  • 小對象 (小于 85,000字節(jié))

  • 大對象 (大于或等于 85,000字節(jié))

原則上小對象和大對象都可以用同樣的方式處理, 但因為壓縮(Compacting)大對象的代價會比較昂貴所以GC會區(qū)分對待.

當GC把一段內(nèi)存交給分配器時, 它會參照分配上下文(Allocation Context).
分配上下文的大小取決于分配單位(Allocation Quantum)的定義.

  • 分配上下文是在堆段(Heap Segment)中專門給指定線程使用的小區(qū)域, 在單核計算機(指單邏輯核心)中只會使用一個上下文, 即第0世代(Generation 0)分配上下文.

  • 分配單位是分配器每次申請的內(nèi)存大小, 用于在分配上下文中給對象分配內(nèi)存. 分配單位通常為8K且受管理對象(Managed Object)的大小通常為32字節(jié), 使得一個分配單位可以用于多個對象的分配 (譯者注: 原理和內(nèi)存池相同)

大對象不會使用分配上下文和分配單位, 因為一個大對象本身可以大于這些小區(qū)域.
并且使用這些區(qū)域帶來的好處(會在下面討論)僅僅受限于小對象.
大對象會直接在堆段(Heap Segment)中分配.

分配器的設計要求實現(xiàn):

  • 在適當時機觸發(fā)GC:?如果超過了分批預算(由收集器設置的閾值)或分配器不能在指定的堆段上分配時將會觸發(fā)GC, 分配預算(Allocation Budget)和受管理的段(Managed Segments)的細節(jié)將在下面討論.

  • 保持對象位置:?如果多個對象在同一個堆段中分配, 它們的虛擬內(nèi)存地址也會鄰接.

  • 高效的使用緩存:?分配器每次都會按?分配單位?申請內(nèi)存, 而不是按每個對象的大小. 在申請內(nèi)存后它會對足夠激活cpu緩存的內(nèi)存大小進行清0, 因為在此之后對象會從這塊內(nèi)存中分配(所以性能會得到提升). 分配單位通常為8K.

  • 高效的避免線程鎖:?因為分配上下文和線程的綁定, 可以保證每個分配單位的內(nèi)存只有對應一個線程可以操作. 因此只要當前的分配上下文沒有用完, 給對象分配內(nèi)存時就不需要線程鎖.

  • 內(nèi)存完整性:?GC總會把新分配的對象的內(nèi)存清0, 從而防止對象指向隨機的內(nèi)容(未定義的內(nèi)容).

  • 保證堆可爬取(Crawlable):?分配器會在每個分配單位即將用完之際新建一個自由對象(Free Object)填充, 例如一個分配單位剩余30個字節(jié)并且下一個要分配的對象需要40個字節(jié), 則分配器會新建一個30個字節(jié)的自由對象填充原來的分配單位并重新獲取一個新的分配單位

分配器的API

Object* GCHeap::Alloc(size_t size, DWORD flags);Object* GCHeap::Alloc(alloc_context* acontext, size_t size, DWORD flags);

以上的函數(shù)可以用于分配小對象和大對象.
另外還有一個函數(shù)用于強制從大對象的堆(LOH: Large Object Heap)中分配內(nèi)存:

Object* GCHeap::AllocLHeap(size_t size, DWORD flags);

收集器的設計

GC的目標

GC致力于高效的內(nèi)存管理, 只要求程序員付出很小的努力
高效指的是:

  • GC應該足夠頻繁的發(fā)生, 以避免堆中有大量(根據(jù)比例和絕對值)未使用但已分配的對象(垃圾)和多余的內(nèi)存.

  • GC應該盡可能不頻繁的發(fā)生, 以避免過多的消耗cpu時間, 即便頻繁發(fā)生可以讓程序占用更小的內(nèi)存.

  • GC應該是生產(chǎn)性的, 如果一次GC只回收了少量的內(nèi)存那么這次GC和它消耗的cpu周期被浪費了.

  • 每次GC應該足夠快, 許多場景會要求低延遲.

  • 程序員們不需要為了優(yōu)化內(nèi)存的利用對GC了解太多(取決于他們的工作).
    – GC應該調(diào)整自身以適應不同的內(nèi)存使用模式.

受管理的堆(Managed Heap)的邏輯表現(xiàn)

CLR GC把對象分成了不同的世代, 當?shù)?N?世代的垃圾被回收后, 生存的對象會被標記為第?N+1?世代, 這個過程被稱為升級(Promotion).
還有一些例外的情況當我們決定是否降級或不升級.

對于小對象的堆會分為3個世代: 第0世代(gen0), 第1世代(gen1)和第2世代(gen2).
對于大對象只有1個世代: 第3世代(gen3).
第0世代和第1世代被稱為短暫(對象的生命周期短)的世代.

對于小對象的堆, 世代中的數(shù)字代表了年齡, 第0世代是最年輕的世代.
但不代表第0世代中的對象一定比第1世代和第2世代的對象年輕,?下面將會說明那些例外.
收集一個世代中的垃圾同時也會收集所有比它年輕的世代的垃圾.

原則上大對象可以使用和小對象一樣的處理方式, 但是壓縮大對象的代價會非常的昂貴, 因此大對象和小對象會受到不同的對待.
因為性能上的原因, 大對象只使用了一個世代(第3世代)并且這個世代會和第2世代一起回收垃圾.
因為第2世代和第3世代可以很大, 需要和短暫的世代(第0世代和第1世代)在開銷上劃出邊界.

為對象分配內(nèi)存時總會從最年輕的世代分配 - 對于小對象總會從第0世代分配, 對于大對象總會從第3世代分配(因為只有一個世代).

受管理的堆(Managed Heap)的物理表現(xiàn)

受管理的堆(Managed Heap)是一個包含了受管理的堆段(Managed Heap Segments)的集合.
受管理的堆段是從系統(tǒng)內(nèi)核申請得到的一塊連續(xù)的內(nèi)存空間. (譯者注: 即malloc/brk申請得到的空間)
用于區(qū)別小對象和大對象, 堆段又分為小對象堆段和大對象堆段.
每個堆中的堆段都是相互鏈接在一起的, 至少會有1個小對象堆段和1個大對象堆段 - 它們會在加載CLR時預留.

每個小對象的堆中只有一個短暫的堆段(Ephemeral Segment)用于存放短暫世代(第0世代和第1世代)的對象, 但也有可能包含第2世代的對象.
其他額外的堆段(0或1或更多個)中只會存放第2世代的對象.

每個大對象的堆中有一個或更多個堆段.

堆段中的空間會從較低的地址向較高的地址消耗, 即地址更小的對象比地址更大的對象更老.?這里也有一些例外將在下面說明.

堆段會在需要時向系統(tǒng)申請, 并且在不包含任何生存的對象時刪除.
但是初始的堆段(加載時預留的)會一直保留.

每個堆中每次只會處理一段(而不是全部), 如在回收小對象和分配大對象時.
這樣的設計提供了更好的性能, 因為大對象只會在第2世代回收時一同回收(代價相當昂貴).

堆段會按它們的申請順序鏈接在一起, 鏈中的最后一個堆段一定是短暫的堆段(Ephemeral Segment).
回收的堆段(不包含任何生存的對象)不一定會被刪除, 也可能被作為一個新的短暫的堆段, 這種機制僅在小對象的堆中實現(xiàn).
每次分配大對象都會考慮整個大對象的堆, 而小對象僅僅考慮短暫的堆段.

分配預算(Allocation Budget)

分配預算是一個關聯(lián)于每個世代的邏輯概念, 當世代的大小達到了指定的限制則會觸發(fā)GC.
每個世代的預算值屬性基本取決于該世代的對象的生存率, 如果生存率較高, 那么這個限制會調(diào)大使得下次對該世代的GC會得到一個更好的回收率.

判斷需要回收哪個世代的垃圾

當GC被觸發(fā)時, GC首先需要確定回收哪個世代.
除了分配預算外, 還有這些因素需要考慮:

  • 世代的碎片化程度 – 如果這個世代的碎片化程度比較高, 則收集這個世代將會得到更好的效果

  • 如果系統(tǒng)內(nèi)存占用比較高, 并且每次可以回收到一定的內(nèi)存, GC可能會更積極的去回收.
    這對于防止系統(tǒng)內(nèi)存分頁(把多出的內(nèi)存數(shù)據(jù)轉(zhuǎn)移到硬盤)很重要.

  • 如果短暫的堆段的空間已經(jīng)用完, GC可能會更積極的去回收以防止申請一個新的堆段.

GC的工作流程

標記階段 (Mark phase)

標記階段的目標是尋找所有存活的對象.

多世代收集器的好處是每次只需要去看堆中最近的對象, 而不需要去看歷史生成的所有對象.
當收集短暫世代(第0世代和第1世代)中的對象時, GC需要尋找這些世代中所有存活的對象,
運行引擎(EE)使用中的對象會標記為存活, 此外被其他對象(更老世代的對象)引用的對象也會標記為存活.

GC在標記更老的世代中的對象時會使用卡片(Cards),
JIT的幫助類會在賦值操作時設置卡片, 如果JIT的幫助類看到一個對象在短暫的范圍中它會設置一個包含了源位置的卡片的字節(jié).
在短暫世代的回收中, GC可以只看堆中其余的部分設置的卡片來找到它們對應的對象.
(譯者注: 卡片表用于標記跨代引用,例如只掃代0的時候如果代1引用了代0的對象也要掃卡片表中代1的部分對象)

計劃階段 (Plan phase)

計劃階段會做一個比較來決定使用壓縮(Compaction)還是清掃(Sweeps).
如果壓縮效果更好則GC會開始實際的壓縮, 否則GC會開始清掃.

重定位階段 (Relocate phase)

如果GC決定要壓縮, 那就要移動現(xiàn)有的對象, 指向這些對象的引用都需要被更新.
重定位階段需要找出指向回收世代中的對象的所有引用.
相反, 標記階段僅會參考存活的對象因此不需要考慮弱引用(Weak References).

壓縮階段 (Compact phase)

這個階段的目標非常直接, 因為計劃階段已經(jīng)計算了對象應該移動到的新地址, 壓縮階段會復制對象到這些新地址中.

清掃階段 (Sweep phase)

清掃階段會尋找夾在存活對象中的空余空間(死對象), 并在這些空間里創(chuàng)建一些自由對象.
相鄰的死對象會被合并成一個自由對象.
創(chuàng)建的自由對象會放到?自由對象列表(freelist)?里.

代碼流程

縮寫: (譯者注: 以下不會使用這些縮寫)

  • WKS GC:?工作站GC.

  • SRV GC:?服務器GC

各個模式的舉動

工作站GC - 不啟用并發(fā)式GC

  • 用戶線程超過了分配預算并觸發(fā)了GC.

  • GC調(diào)用SuspendEE函數(shù)來停止所有受管理的線程(Managed Threads).

  • GC決定需要回收哪個世代.

  • 運行標記階段.

  • 運行計劃階段決定是用壓縮還是用清掃.

  • 如果決定壓縮則運行重定位階段和壓縮階段, 否則運行清掃階段.

  • GC調(diào)用RestartEE函數(shù)來恢復受管理的線程.

  • 用戶線程恢復運行.

  • 工作站GC - 啟用并發(fā)式GC

    這里說明了后臺GC如何運作.

  • 用戶線程超過了分配預算并觸發(fā)了GC.

  • GC調(diào)用SuspendEE函數(shù)來停止所有受管理的線程(Managed Threads).

  • GC決定是否使用后臺GC.

  • 如果使用后臺GC, 則后臺GC線程會被喚醒并進行回收工作. 后臺GC線程會調(diào)用RestartEE喚醒受管理的線程.

  • 受管理的線程繼續(xù)運行, 同時后臺GC線程也繼續(xù)它的工作.

  • 用戶線程可能又一次的超過了分配預算并觸發(fā)了一個短暫的GC(我們稱為前臺GC), 流程和"工作站GC - 不啟用并發(fā)式GC"一樣.

  • 后臺GC線程再次調(diào)用SuspendEE函數(shù), 進行標記階段(Marking), 然后調(diào)用RestartEE函數(shù), 再進行可以并行的清掃階段(Sweep).

  • 后臺GC已完成工作.

  • 服務器GC - 不啟用并發(fā)式GC

  • 用戶線程超過了分配預算并觸發(fā)了GC.

  • 服務器GC線程被喚醒, 并調(diào)用SuspendEE來停止所有受管理的線程(Managed Threads).

  • 服務器GC進行回收工作(流程和工作站GC - 不啟用并發(fā)式GC相同).

  • 服務器GC線程調(diào)用RestartEE喚醒受管理的線程.

  • 用戶線程恢復運行.

  • 服務器GC - 啟用并發(fā)式GC

    流程和工作站GC - 啟用并發(fā)式GC一樣, 除了非后臺的GC也會在服務器GC線程中完成.

    物理架構(gòu)

    這一段旨在幫助你追蹤代碼流程.

    當用戶線程用完分配單位(Allocation Quantum), 需要一個新的分配單位時會調(diào)用try_allocate_more_space函數(shù).

    當try_allocate_more_space函數(shù)需要觸發(fā)GC時會調(diào)用GarbageCollectGeneration函數(shù).

    在"工作站GC - 不啟用并發(fā)式GC"的模式下, GarbageCollectGeneration會在觸發(fā)GC的用戶線程上完成所有工作, 代碼流程是:

    GarbageCollectGeneration(){SuspendEE();garbage_collect();RestartEE();}garbage_collect(){generation_to_condemn();gc1();}gc1(){mark_phase();plan_phase();}plan_phase(){ ? ? // 實際的計劃階段, 判斷要用壓縮還是清掃if (compact){relocate_phase();compact_phase();} ? ? else ? ? ? ? make_free_lists();}

    在"工作站GC - 啟用并發(fā)式GC"的模式(默認模式)下, 后臺GC的代碼流程是:

    GarbageCollectGeneration(){SuspendEE();garbage_collect();RestartEE();}garbage_collect(){generation_to_condemn(); ? ? // 判斷要用后臺GC, 喚醒后臺GCdo_background_gc();}do_background_gc(){init_background_gc();start_c_gc (); // 等待后臺GC完成工作并重啟受管理的線程wait_to_proceed();}bgc_thread_function() { ? ? while (1){ ? ? ? ? // 等待事件// 喚醒gc1();}}gc1(){background_mark_phase();background_sweep();}

    資源鏈接

    • .NET CLR GC Implementation

    • The Garbage Collection Handbook: The Art of Automatic Memory Management

    • Garbage collection (Wikipedia)

    譯者后注

    這份文檔簡單的介紹了GC的設計和工作流程, 同時也帶來和很多疑問, 例如一個程序有多少個堆和什么是卡片等
    我將在CoreCLR源碼探索的系列文章中分析CoreCLR的GC源碼來解決這些疑問
    另外博客園上已經(jīng)有一位大神對CoreCLR的GC源碼進行了部分分析,可以查看他的博客:http://www.cnblogs.com/kmsfan/p/5514473.html?

    相關文章:

    • 《代碼的未來》讀書筆記:內(nèi)存管理與GC那點事兒

    • CoreCLR源碼探索(一) Object是什么

    • CoreCLR源碼探索(二) new是什么

    • CoreCLR源碼探索(三) GC內(nèi)存分配器的內(nèi)部實現(xiàn)

    • .NET跨平臺之旅:corehost 是如何加載 coreclr 的

    • .NET CoreCLR開發(fā)人員指南(上)

    原文地址:http://www.cnblogs.com/zkweb/p/6288457.html


    .NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

    總結(jié)

    以上是生活随笔為你收集整理的CoreCLR文档翻译 - GC的设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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