1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        面試必備:C#中堆和棧的區(qū)別和分析

        共 3651字,需瀏覽 8分鐘

         ·

        2021-05-25 21:29

        ????棧:由編譯器自動(dòng)分配、釋放。在函數(shù)體中定義的變量通常在棧上。堆:一般由程序員分配釋放。用 new、malloc等分配內(nèi)存函數(shù)分配得到的就是在堆上。存放在棧中時(shí)要管存儲(chǔ)順序,保持著先進(jìn)后出的原則,他是一片連續(xù)的內(nèi)存域,有系統(tǒng)自動(dòng)分配和維 護(hù);

        ????堆:是無序的,他是一片不連續(xù)的內(nèi)存域,有用戶自己來控制和釋放,如果用戶自己不釋放的話,當(dāng)內(nèi) 存達(dá)到一定的特定值時(shí),通過垃圾回收器(GC)來回收。棧內(nèi)存無需我們管理,也不受GC管理。當(dāng)棧頂元素使用完畢,立馬釋放。而堆則需要GC清理。使用引用類型的時(shí)候,一般是對(duì)指針進(jìn)行的操作而非引用類型對(duì)象本身。但是值類型則操作其本身

        詳解:

        線程堆棧:簡(jiǎn)稱棧 Stack
        托管堆:簡(jiǎn)稱堆 Heap

        使用.Net框架開發(fā)程序的時(shí)候,我們無需關(guān)心內(nèi)存分配問題,因?yàn)橛蠫C這個(gè)大管家給我們料理一切。如果我們寫出如下兩段代碼:

        bf17bb244a559b5477b6584d06f62699.webp

        1 代碼段1:
        2
        3 public int AddFive(int pValue)
        4 {
        5 int result;
        6 result = pValue + 5;
        7 return result;
        8 }

        bf17bb244a559b5477b6584d06f62699.webp

        bf17bb244a559b5477b6584d06f62699.webp

         1 代碼段2:
        2
        3 public class MyInt
        4 {
        5 public int MyValue;
        6 }
        7
        8 public MyInt AddFive(int pValue)
        9 {
        10 MyInt result = new MyInt();
        11 result.MyValue = pValue + 5;
        12 return result;
        13 }

        bf17bb244a559b5477b6584d06f62699.webp

        問題1:你知道代碼段1在執(zhí)行的時(shí)候,pValue和result在內(nèi)存中是如何存放,生命周期又如何?代碼段2呢?
        要想釋疑以上問題,我們就應(yīng)該對(duì).Net下的棧(Stack)和托管堆(Heap)(簡(jiǎn)稱堆)有個(gè)清楚認(rèn)識(shí),本立而道生。如果你想提高程序性能,理解棧和堆,必須的!
        本文就從棧和堆,類型變量展開,對(duì)我們寫的程序進(jìn)行庖丁解牛。
        C#程序在CLR上運(yùn)行的時(shí)候,內(nèi)存從邏輯上劃分兩大塊:棧,堆。這倆基本元素組成我們C#程序的運(yùn)行環(huán)境。

        一,棧 vs 堆:區(qū)別?

        棧通常保存著我們代碼執(zhí)行的步驟,如在代碼段1中 AddFive()方法,int pValue變量,int result變量等等。而堆上存放的則多是對(duì)象,數(shù)據(jù)等。(譯者注:忽略編譯器優(yōu)化)我們可以把棧想象成一個(gè)接著一個(gè)疊放在一起的盒子。當(dāng)我們使用的時(shí)候,每次從最頂部取走一個(gè)盒子。棧也是如此,當(dāng)一個(gè)方法(或類型)被調(diào)用完成的時(shí)候,就從棧頂取走(called a Frame,譯注:調(diào)用幀),接著下一個(gè)。堆則不然,像是一個(gè)倉(cāng)庫(kù),儲(chǔ)存著我們使用的各種對(duì)象等信息,跟棧不同的是他們被調(diào)用完畢不會(huì)立即被清理掉

        棧內(nèi)存無需我們管理,也不受GC管理。當(dāng)棧頂元素使用完畢,立馬釋放。而堆則需要GC(Garbage collection:垃圾收集器)清理。


        二,什么元素被分配到棧?什么被分配到堆?

        當(dāng)我們程序執(zhí)行的時(shí)候,在棧和堆中分配有四種主要的類型:值類型,引用類型,指針,指令。

        值類型:
        在C#中,繼承自System.ValueType的類型被稱為值類型,主要有以下幾種(CLR2.0中支持類型有增加):

        * bool
        * byte
        * char
        * decimal
        * double
        * enum
        * float
        * int
        * long
        * sbyte
        * short
        * struct
        * uint
        * ulong
        * ushort

        引用類型:
        以下是引用類型,繼承自System.Object:
        * class
        * interface
        * delegate
        * object
        * string

        指針:
        在內(nèi)存區(qū)中,指向一個(gè)類型的引用,通常被稱為“指針”,它是受CLR( Common Language Runtime:公共語言運(yùn)行時(shí))管理,我們不能顯示使用。需要注意的是,一個(gè)類型的引用即指針跟引用類型是兩個(gè)完全不同的概念。指針在內(nèi)存中占一塊內(nèi)存區(qū),它本身只代表一個(gè)內(nèi)存地址(或者null),它所指向的另一塊內(nèi)存區(qū)才是我們真正的數(shù)據(jù)或者類型。

        指令:
        后文對(duì)指令再做介紹。

        三,如何分配?
        我們先看一下兩個(gè)觀點(diǎn):
        觀點(diǎn)1,引用類型總是被分配在堆上。(正確?)
        觀點(diǎn)2,值類型和指針總是分配在被定義的地方,他們不一定被分配到棧上。(這個(gè)理解起來有點(diǎn)難度,需要慢慢來)

        上文提及的棧(Stack),在程序運(yùn)行的時(shí)候,每個(gè)線程(Thread)都會(huì)維護(hù)一個(gè)自己的專屬線程堆棧。
        當(dāng)一個(gè)方法被調(diào)用的時(shí)候,主線程開始在所屬程序集的元數(shù)據(jù)中,查找被調(diào)用方法,然后通過JIT即時(shí)編譯并把結(jié)果(一般是本地CPU指令)放在棧頂。CPU通過總線從棧頂取指令,驅(qū)動(dòng)程序以執(zhí)行下去。

        下面我們以實(shí)例來詳談。

        還是我們開篇所列的代碼段1:

        bf17bb244a559b5477b6584d06f62699.webp

        1 public int AddFive(int pValue)
        2 {
        3 int result;
        4 result = pValue + 5;
        5 return result;
        6 }

        bf17bb244a559b5477b6584d06f62699.webp

        當(dāng)AddFive方法開始執(zhí)行的時(shí)候,方法參數(shù)(parameters)則在棧上分配。

        接著,指令指向AddFive方法內(nèi)部,如果該方法是第一次執(zhí)行,首先要進(jìn)行JIT即時(shí)編譯。

        當(dāng)方法內(nèi)部開始執(zhí)行的時(shí)候,變量result被分配在棧上,方法執(zhí)行完畢,而且方法返回后,棧上的區(qū)域被清理。

        以上看出,一個(gè)值類型變量,一般會(huì)分配在棧上。那觀點(diǎn)2中所述又做何理解?“值類型和指針總是分配在被定義的地方,他們不一定被分配到棧上”。
        原因就是如果一個(gè)值類型被聲明在一個(gè)方法體外并且在一個(gè)引用類型中,那它就會(huì)在堆上進(jìn)行分配。
        還是代碼段2:

        bf17bb244a559b5477b6584d06f62699.webp

         1 public class MyInt
        2 {
        3 public int MyValue;
        4 }
        5
        6 public MyInt AddFive(int pValue)
        7 {
        8 MyInt result = new MyInt();
        9 result.MyValue = pValue + 5;
        10 return result;
        11 }

        bf17bb244a559b5477b6584d06f62699.webp

        當(dāng)線程開始執(zhí)行AddFive方法的時(shí)候,參數(shù)被分配到棧上,由于MyInt是一個(gè)引用類型,所以它被分配到堆上,并且在棧中生成一個(gè)指針(result),AddFive方法執(zhí)行完畢時(shí),棧上內(nèi)存被清理,堆中依然存在。

        ????當(dāng)程序需要更多的堆空間時(shí),GC需要進(jìn)行垃圾清理工作,暫停所有線程,找出所有不可達(dá)到對(duì)象,即無被引用的對(duì)象,進(jìn)行清理。并通知棧中的指針重新指向地址排序后的對(duì)象。現(xiàn)在我們應(yīng)該知道,了解棧和堆,對(duì)我們開發(fā)出高性能程序的重要性。當(dāng)我們使用引用類型的時(shí)候,一般是對(duì)指針進(jìn)行的操作而非引用類型對(duì)象本身。但是值類型則操作其本身。
        接下來,我們用例子說明這一點(diǎn)。

        bf17bb244a559b5477b6584d06f62699.webp

         1 例1:
        2
        3 public int ReturnValue()
        4 {
        5 int x = new int();
        6 x = 3;
        7 int y = new int();
        8 y = x;
        9 y = 4;
        10 return x;
        11 }

        bf17bb244a559b5477b6584d06f62699.webp

        執(zhí)行結(jié)果為3,稍作修改:

        bf17bb244a559b5477b6584d06f62699.webp

         1 例2:
        2
        3 public class MyInt
        4 {
        5 public int MyValue;
        6 }
        7
        8 public int ReturnValue2()
        9 {
        10 MyInt x = new MyInt();
        11 x.MyValue = 3;
        12 MyInt y = new MyInt();
        13 y = x;
        14 y.MyValue = 4;
        15 return x.MyValue;
        16 }

        bf17bb244a559b5477b6584d06f62699.webp

        執(zhí)行結(jié)果為4。

        我們來分析下原因,其實(shí)例1的跟以下代碼所起效用一樣:

        bf17bb244a559b5477b6584d06f62699.webp

        1 public int ReturnValue()
        2 {
        3 int x = 3;
        4 int y = x;
        5 y = 4;
        6 return x;
        7 }

        bf17bb244a559b5477b6584d06f62699.webp

        在棧上x和y分別占用一塊內(nèi)存區(qū),互不干擾。

        而例2,與以下代碼所起效用一樣:

        bf17bb244a559b5477b6584d06f62699.webp

        1 public int ReturnValue2()
        2 {
        3 MyInt x;
        4 x.MyValue = 3;
        5 MyInt y;
        6 y = x;
        7 y.MyValue = 4;
        8 return x.MyValue;
        9 }

        bf17bb244a559b5477b6584d06f62699.webp

        引用:https://www.cnblogs.com/yplong/p/3467025.html



        版權(quán)申明:本文來源于網(wǎng)友收集或網(wǎng)友提供,如果有侵權(quán),請(qǐng)轉(zhuǎn)告版主或者留言,本公眾號(hào)立即刪除。


        支持小微:

        騰訊云 搞活動(dòng)了?玩服務(wù)器的可以搞搞。就這幾天時(shí)間。

        輕量?1C2G 50GB SSD盤 255元/3年

        鏈接:https://curl.qcloud.com/qINmPBX9


        右下角,您點(diǎn)一下在看圖片15c6bd3dace1157faa5e1e4af9548475.webp

        小微工資漲1毛

        商務(wù)合作QQ:185601686


        收錄于話題?#C#知識(shí)學(xué)習(xí)

        10個(gè)

        下一篇C#怎么獲取兩個(gè)數(shù)組或集合的交集或差集?


        瀏覽 45
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            北条麻妃无码中文 | 玖玖玖免费视频 | 天天性综合网 | gayspank男男打屁股网站 | 国产一级二级三级在线 | 五月天婷婷综合网 | 欧美婷婷精品激情 | 天天好屄网 | 猛操骚逼| 久久久久久国产视频 |