欧美成人精品手机在线观看_69视频国产_动漫精品第一页_日韩中文字幕网 - 日本欧美一区二区

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

.NET Core 堆結(jié)構(gòu)(Heap)底層原理淺談

freeflydom
2024年12月14日 10:51 本文熱度 443

.Net托管堆布局

加載堆

主要是供CLR內(nèi)部使用,作為承載程序的元數(shù)據(jù)。

  1. HighFrequencyHeap
    存放CLR高頻使用的內(nèi)部數(shù)據(jù),比如MethodTable,MethodDesc.

通過(guò)is判斷類(lèi)型之間的繼承關(guān)系,調(diào)用接口的方法和虛方法,都需要訪問(wèn)MethodTable

  1. LowFrequencyHeap
    存放CLR低頻使用的內(nèi)部數(shù)據(jù),比如EEClass,ClassLoader.

GC信息與異常處理表,它們都只在發(fā)生時(shí)才訪問(wèn),因此訪問(wèn)頻率不高。

  1. StringLiteralMap
    字符串駐留池:https://www.cnblogs.com/lmy5215006/p/18494483

字符串對(duì)象本身存儲(chǔ)在FOH堆中,String Literal Map只是一個(gè)索引

  1. StubHeap
    函數(shù)入口的代碼堆
  2. CodeHeap
    JIT編譯代碼使用的內(nèi)部堆,比如生成IL。
  3. VirtualCallStubHeap
    虛方法調(diào)用的內(nèi)部堆

使用!eeheap -loader可以查看

眼見(jiàn)為實(shí)

新版sos呈現(xiàn)方式不一樣,可以使用老版sos展示文中所述內(nèi)容

托管堆

大家的老朋友了,不做過(guò)多解釋?zhuān)蒅C統(tǒng)一管理的內(nèi)存堆.一個(gè).NET程序中所有的Domain都會(huì)共用一個(gè)托管堆

  1. SOH
    略略略
  2. LOH
    略略略
  3. POH
    固定對(duì)象專(zhuān)屬的堆,比如非托管線(xiàn)程訪問(wèn)托管對(duì)象,就需要把對(duì)象固定起來(lái),避免被GC回收造成非托管代碼的訪問(wèn)違例.

使用!eeheap -gc可以查看

眼見(jiàn)為實(shí)

凍結(jié)堆

.NET8推出來(lái)的一個(gè)新堆,用來(lái)存放永遠(yuǎn)不會(huì)被GC管理的永生對(duì)象,比如string 字面量。
簡(jiǎn)單來(lái)說(shuō),就是一個(gè)對(duì)象你都永遠(yuǎn)不會(huì)釋放了,還放在托管堆就是浪費(fèi)了。不如單獨(dú)拎出來(lái)存。

眼見(jiàn)為實(shí)

https://www.cnblogs.com/lmy5215006/p/18515971

上述所說(shuō)的各種堆,只是一個(gè)邏輯上的概念。作為內(nèi)存的物理承載。由堆段(Heap Seg-ment)實(shí)現(xiàn).
簡(jiǎn)單來(lái)說(shuō),段是托管堆的物理表示。

眼見(jiàn)為實(shí)

segmentbeginallocatedcommitted allocated sizecommitted size
段指針的對(duì)象地址內(nèi)存分配的起始點(diǎn)內(nèi)存分配的末尾點(diǎn)已提交的分配大小已提交的大小

SOH小對(duì)象堆

堆只是一個(gè)抽象的概念,在物理上的表現(xiàn)形式為內(nèi)存段,作為CLR細(xì)化堆的一種管理單位。多個(gè)段組成了堆。

.NET8之前的段結(jié)構(gòu)

在.NET 8 之前,段分為SOH,LOH,POH 三個(gè)段。
對(duì)于SOH段有點(diǎn)特殊,因?yàn)槎紊厦孢€有分代邏輯。包含0代和1代的對(duì)象只會(huì)分配在新分配的內(nèi)存段上(臨時(shí)段),剩下的每個(gè)段都是2代的段

可以看到,代只是一個(gè)邏輯概念,并沒(méi)有獨(dú)立的段空間。0,1,2代共享段空間。

.NET8的段結(jié)構(gòu)

到了.NET 8,代已經(jīng)不是一個(gè)邏輯概念,而是一個(gè)物理概念。
每個(gè)代都有了自己獨(dú)立的段空間。

代機(jī)制

每當(dāng)GC觸發(fā)時(shí),所有對(duì)象(非固定)都會(huì)進(jìn)行升代,直到gen2為止。

  1. obj對(duì)象剛創(chuàng)建,為0代
    內(nèi)存地址為0x00000263ee009528,0x01fb08000028>0x000001fb080b71e0>01fb080b9068 說(shuō)明obj放在0代里
  2. 第一次GC,obj升為1代
    內(nèi)存地址在1代空間范圍內(nèi)
  3. 第二次GC,obj升為2代
    內(nèi)存地址在2代空間范圍內(nèi)

代邊界

細(xì)心的朋友會(huì)發(fā)現(xiàn)一個(gè)盲點(diǎn),就是obj剛剛創(chuàng)建的時(shí)候,0代內(nèi)存起始點(diǎn)為0263ee000028,升為1代后,1代內(nèi)存起始點(diǎn)也變?yōu)榱?263ee000028,2代也同樣。
這就引申出另一個(gè)概念,GC升代,不是簡(jiǎn)單的copy對(duì)象從0代到1代。而是移動(dòng)代的邊界。
每次GC觸發(fā)時(shí),代邊界指針會(huì)在多個(gè)“地址段”上遷移,通過(guò)這種邏輯操作,達(dá)到性能的最高,可以觀察上面的 Allocated 區(qū),一會(huì)給了 0gen,一會(huì)又給了 1gen,一會(huì)又給了 2gen

LOH大對(duì)象堆

大對(duì)象堆存儲(chǔ)所有>=85000byte的對(duì)象,但也是有例外。LOH堆上對(duì)象管理相對(duì)寬松,沒(méi)有“代”機(jī)制,默認(rèn)情況下也不會(huì)壓縮。

例外1-32位環(huán)境下的double[]

        static void Main(string[] args)
        {
            double[] array1 = new double[999];
            Console.WriteLine(GC.GetGeneration(array1));
            double[] array2 = new double[1000];
            Console.WriteLine(GC.GetGeneration(array2));
            double[,] array3 = new double[32,32];
            Console.WriteLine(GC.GetGeneration(array3));
            long[] array4 = new long[1000];
            Console.WriteLine(GC.GetGeneration(array4));
            Debugger.Break();
            Console.ReadKey();
        }

這里有個(gè)很奇怪的現(xiàn)象,在32位環(huán)境下,array2的大小= 4b+4+4+1000*8=8012byte. 遠(yuǎn)遠(yuǎn)<=85000byte. 為什么被分配到了LOH堆?
這主要跟內(nèi)存對(duì)齊有關(guān),double的未對(duì)齊訪問(wèn)非常昂貴,遠(yuǎn)遠(yuǎn)超過(guò)long,ulong,int。這對(duì)于64位環(huán)境來(lái)說(shuō)不是問(wèn)題,總是對(duì)SOH與LOH使用8byte對(duì)齊。但對(duì)于4字節(jié)對(duì)齊的32位環(huán)境。這就是個(gè)大問(wèn)題了.
所以CLR開(kāi)發(fā)團(tuán)隊(duì)決定將閾值大于1000的double放入LOH堆(LOH堆總是8byte對(duì)齊)。避免了double未對(duì)齊訪問(wèn)的巨大成本

例外2-StringInter與靜態(tài)成員以及元數(shù)據(jù)

https://www.cnblogs.com/lmy5215006/p/18515971
參考此文,在.NET5之前沒(méi)有POH堆,所以CLR內(nèi)部使用的三個(gè)數(shù)組也會(huì)進(jìn)入LOH堆。
三個(gè)數(shù)組分別為

  1. static對(duì)象的object[]
  2. 字符串池 object[]
  3. 元數(shù)據(jù) RuntimeType object[]

其實(shí)很好理解,這些都是低頻變化的內(nèi)容,放在LOH堆上好過(guò)放在SOH堆。

POH堆

POH堆解決了什么問(wèn)題?
從.NET5開(kāi)始,CLR團(tuán)隊(duì)給pinned的對(duì)象單獨(dú)放入一個(gè)段中,這樣pinned對(duì)象不會(huì)和普通對(duì)象混在一起。導(dǎo)致大量細(xì)小Free空間。從而降低托管堆碎片化,也降低了代降級(jí)的頻次。

有點(diǎn)遺憾的是,非托管代碼造成的對(duì)象固定,并不會(huì)移動(dòng)到POH堆中。因此代降級(jí)的現(xiàn)象依舊存在。
感覺(jué)未來(lái)微軟可以重點(diǎn)優(yōu)化這塊,固定對(duì)象是GC速度最大的阻礙。

如何使用POH堆?

在.NET 8中,將對(duì)象放入POH堆是一種“有意為之”行為,必須調(diào)用 GC 類(lèi)提供的 AllocateArray 和 AllocateUninitializedArray 方法并設(shè)置 pinned=true

FOH

FOH堆解決了什么問(wèn)題?
在.NET8中,如果一個(gè)對(duì)象在創(chuàng)建的時(shí)候,就明確知道是“永生”對(duì)象,那就沒(méi)必要納入托管堆的管理范圍,只會(huì)徒增GC的工作量。因此干脆把對(duì)象放在托管堆之外,來(lái)提高性能

常見(jiàn)的例子就是字符串的字面量(literal)

static對(duì)象布局,不會(huì)被GC回收的對(duì)象1

靜態(tài)的基元類(lèi)型(short,int,long) ,它的值本身并不存放在托管堆上。而是存放在Domain中的高頻堆中

靜態(tài)的引用類(lèi)型則不同。真正的對(duì)象存放在托管堆上,再由POH中一個(gè)object[]持有,最后被高頻堆中的m_pGCStatics所管理

Domain下每一個(gè)Module都維護(hù)了一個(gè)DomainLocalModule結(jié)構(gòu),靜態(tài)變量放在該Module中

眼見(jiàn)為實(shí):靜態(tài)基元類(lèi)型分配在高頻堆上?

    internal class Program
    {
        static long age = 10086;
        static void Main(string[] args)
        {
            age = 12;
            Console.WriteLine("done. " + age);
            Debugger.Break();
        }
    }


通過(guò)匯編得知,static a的地址為00007ff9a618e4a8

觀察高頻堆地址可以發(fā)現(xiàn),00007FF9A6180000<00007ff9a618e4a8<00007FF9A6190000 。明顯屬于高頻堆

眼見(jiàn)為實(shí):靜態(tài)引用類(lèi)型分配在哪?

    internal class Program
    {
        public static Person person = new Person();
        static void Main(string[] args)
        {
            var num = person.age;
            Console.WriteLine(num);
            Debugger.Break();
        }
    }
    public class Person
    {
        public int age = 12;
    }
  1. 使用!gcwhere命令來(lái)查看person對(duì)象屬于0代中,說(shuō)明對(duì)象本身分配在托管堆

  2. 使用!gcroot命令查看它的引用根,發(fā)現(xiàn)它被一個(gè)object[]所持有

  3. 再查看object[]的所屬代,可以看到該對(duì)象屬于POH堆

  4. bp coreclr!JIT_GetSharedNonGCStaticBase_Helper 下斷點(diǎn)來(lái)獲取 DomainLocalModule 實(shí)例

    注意,這里我重新運(yùn)行了一遍,所以object[]內(nèi)存地址有變

字符串駐留池布局,不會(huì)被GC回收的對(duì)象2

關(guān)于字符串的不可變性,參考此文:https://www.cnblogs.com/lmy5215006/p/18494483

在.NET8之前,字符串駐留與靜態(tài)引用類(lèi)型處理模式無(wú)差別。
.NET 8加入FOH堆之后,會(huì)將編譯期間就能確定的字符串放入FOH堆,以便提高GC性能。

眼見(jiàn)為實(shí)

        static void Main(string[] args)
        {
            var str1 = "hello FOH";//編譯期間能確定
            var str2 = Console.ReadLine();
            string.Intern(str2);//運(yùn)行期間才能確定
            Console.WriteLine($"str1={str1},str2={str2}");
            Debugger.Break();
        }
  1. 編譯期間能確定的,直接加入了FOH

  2. 運(yùn)行期間確定,與靜態(tài)引用類(lèi)型處理流程一致

轉(zhuǎn)自https://www.cnblogs.com/lmy5215006/p/18583743


該文章在 2024/12/14 10:51:40 編輯過(guò)
關(guān)鍵字查詢(xún)
相關(guān)文章
正在查詢(xún)...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專(zhuān)業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車(chē)隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類(lèi)企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷(xiāo)售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶(hù)的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved