.NET中六個重要的概念:棧、堆、實值型別、參考型別、裝箱和拆箱

來源:互聯網
上載者:User

標籤:blog   http   ar   使用   strong   sp   資料   div   on   

 作者: Edison Chou  來源: 部落格園  發布時間: 2014-09-03 15:59  閱讀: 318 次  推薦: 2   原文連結   [收藏]  

  原文Shivprasad koirala

  英文原文:Six important .NET concepts: Stack, heap, value types, reference types, boxing, and unboxing

  一、概述

  本文會闡述六個重要的概念:堆、棧、實值型別、參考型別、裝箱和拆箱。本文首先會通過闡述當你定義一個變數之後系統內部發生的改變開始講解,然後將關注點轉移到儲存雙雄:堆和棧。之後,我們會探討一下實值型別和參考型別,並對有關於這兩種類型的重要基礎內容做一個講解。

  本文會通過一個簡單的代碼來展示在裝箱和拆箱過程中所帶來的效能上的影響,請各位仔細閱讀。

  二、當你聲明一個變數背後發生了什嗎?

  當你在一個.NET應用程式中定義一個變數時,在RAM中會為其分配一些記憶體塊。這塊記憶體有三樣東西:變數的名稱變數的資料類型以及變數的值

  上面簡單闡述了記憶體中發生的事情,但是你的變數究竟會被分配到哪種類型的記憶體取決於資料類型。在.NET中有兩種可分配的記憶體:。在接下來的幾個部分中,我們會試著詳細地來理解這兩種類型的儲存。

  三、儲存雙雄:堆和棧

  為了理解棧和堆,讓我們通過以下的代碼來瞭解背後到底發生了什麼。

public void Method1(){    // Line 1    int i=4;    // Line 2    int y=2;    //Line 3    class1 cls1 = new class1();}

  代碼只有三行,現在我們可以一行一行地來瞭解到底內部是怎麼來執行的。

  • Line 1:當這一行被執行後,編譯器會在棧上分配一小塊記憶體。棧會在負責跟蹤你的應用程式中是否有運行記憶體需要
  • Line 2:現在將會執行第二步。正如棧的名字一樣,它會將此處的一小塊記憶體配置疊加在剛剛第一步的記憶體配置的頂部。你可以認為棧就是一個一個疊加起來的房間或盒子。在棧中,資料的分配和解除都會通過LIFO (Last In First Out)即先進後出的邏輯規則進行。換句話說,也就是最先進入棧中的資料項目有可能最後才會出棧。
  • Line 3:在第三行中,我們建立了一個對象。當這一行被執行後,.NET會在棧中建立一個指標,而實際的對象將會儲存到一個叫做“堆”的記憶體地區中。“堆”不會監測運行記憶體,它只是能夠被隨時訪問到的一堆對象而已。不同於棧,堆用於動態記憶體的分配。
  • 這裡需要注意的另一個重要的點是對象的引用指標是分配在棧上的。 例如:聲明語句 Class1 cls1; 其實並沒有為Class1的執行個體分配記憶體,它只是在棧上為變數cls1建立了一個引用指標(並且將其預設職位null)。只有當其遇到new關鍵字時,它才會在堆上為對象分配記憶體。
  • 離開這個Method1方法時(the fun):現在執行控制語句開始離開方法體,這時所有在棧上為變數所分配的記憶體空間都會被清除。換句話說,在上面的樣本中所有與int類型相關的變數將會按照“LIFO”後進先出的方式從棧中一個一個地出棧。
  • 需要注意的是:這時它並不會釋放堆中的記憶體塊,堆中的記憶體塊將會由記憶體回收行程稍候進行清理。

  現在我們許多的開發人員朋友一定很好奇為什麼會有兩種不同類型的儲存?我們為什麼不能將所有的記憶體塊分配只到一種類型的儲存上?

  如果你觀察足夠仔細,基中繼資料類型並不複雜,他們僅僅儲存像 ‘int i = 0’這樣的值。對象資料類型就複雜了,他們引用其他對象或其他基中繼資料類型。換句話說,他們儲存其他多個值的引用並且這些值必須一一地儲存在記憶體中。物件類型需要的是動態記憶體而基元類型需要靜態記憶體。如果需求是動態記憶體的話,那麼它將會在堆上為其分配記憶體,相反,則會在棧上為其分配。

  四、實值型別和參考型別

  既然我們已經瞭解了棧和堆的概念了,是時候瞭解實值型別和參考型別的概念了。實值型別將資料和記憶體都儲存在同一位置,而一個參考型別則會有一個指向實際記憶體地區的指標。

  通過,我們可以看到一個名為i的整形資料類型,它的值被賦值到另一個名為j的整形資料類型。他們的值都被儲存到了棧上。

  當我們將一個int類型的值賦值到另一個int類型的值時,它實際上是建立了一個完全不同副本。換句話說,如果你改變了其中某一個的值,另一個不會發生改變。於是,這些種類的資料類型被稱為“實值型別”。

  當我們建立一個對象並且將此對象賦值給另外一個對象時,他們彼此都指向了如程式碼片段所示的記憶體中同一塊地區。因此,當我們將obj賦值給obj1時,他們都指向了堆中的同一塊地區。換句話說,如果此時我們改變了其中任何一個,另一個都會受到影響,這也說明了他們為何被稱為“參考型別”。

  五、哪些是實值型別,哪些是參考型別?

  在.NET中,變數是儲存到棧還是堆中完全取決於其所屬的資料類型。比如:‘String’或‘Object’屬於參考型別,而其他.NET基中繼資料類型則會被分配到棧上。則詳細地展示了在.NET預置類型中,哪些是實值型別,哪些又是參考型別。

  六、裝箱和拆箱

  現在,你已經有了不少的理論基礎了。現在,是時候瞭解上面的知識在實際編程中的使用了。在應用中最大的一個意義就在於:理解資料從棧移動到堆的過程中所發生的效能消耗問題,反之亦然。

  考慮一下以下的程式碼片段,當我們將一個實值型別轉換為參考型別,資料將會從棧移動到堆中。相反,當我們將一個參考型別轉換為實值型別時,資料也會從堆移動到棧中。

  不管是在從棧移動到堆還是從堆中移動到棧上都會不可避免地對系統效能產生一些影響。

  於是,兩個新名詞橫空出世:當資料從實值型別轉換為參考型別的過程被稱為“裝箱”,而從參考型別轉換為實值型別的過程則被成為“拆箱”。

  如果你編譯一下上面這段代碼並且在ILDASM(一個IL的反編譯工具)中對其進行查看,你會發現在IL代碼中,裝箱和拆箱是什麼樣子的。則展示了範例程式碼被編譯後所產生的IL代碼。

  七、裝箱和拆箱的效能問題

  為了弄明白到底裝箱和拆箱會帶來怎樣的效能影響,我們分別迴圈運行10000次所示的兩個函數方法。其中第一個方法中有裝箱操作,另一個則沒有。我們使用一個Stopwatch對象來監視時間的消耗。

  具有裝箱操作的方法花費了3542毫秒來執行完成,而沒有裝箱操作的方法只花費了2477毫秒,整整相差了1秒多。而且,這個值也會因為迴圈次數的增加而增加。也就是說,我們要盡量避免裝箱和拆箱操作。在一個項目中,如果你需要裝箱和裝箱,請仔細考慮它是否是絕對必不可少的操作,如果不是,那麼盡量不用。

  雖然以上程式碼片段沒有展示拆箱操作,但其效果同樣適用於拆箱。你可以通過寫代碼來實現拆箱,並且通過Stopwatch來測試其時間消耗。

.NET中六個重要的概念:棧、堆、實值型別、參考型別、裝箱和拆箱 (轉)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.