淺談值類型和引用類型在堆和棧中的存儲一


首先,讓我們來簡單了解一下什么是“棧”(stack),什么是“堆”(heap)。“棧”其實就是一種后入先出(LIFO)的數據結構。在我們.NET Framework里面,由CLR負責管理,我們程序員不用去擔心垃圾回收的問題;每一個線程都有自己的專屬的“棧”。“堆”的存放就要零散一些,並且由 Garbage Collector(GC)執行管理,我們關注的垃圾回收部分,就是在“堆”上的垃圾回收;其次就是整個進程共用一個“堆”。

 

 

我們先來記住兩條黃金法則:

1.引用類型總是被分配到“堆”上。

2.值類型總是分配到它聲明的地方:

   a.作為引用類型的成員變量分配到“堆”上

   b.作為方法的局部變量時分配到“棧”上

 

要真正理解上面的兩條黃金法則,還需要了解一下,“棧”和“堆”是如何工作的。首先以下面的這個方法為例:

public int AddFive(int pValue)
{
  int result; 
  result = pValue + 5; 
  return result;
}

1.方法AddFive()被壓入“棧”

2.緊接着方法參數pValue被壓入“棧”

3.然后是需要為result變量分配空間,這時被分配到“棧”上。

4.最后返回結果

通過將棧指針指向 AddFive()方法曾使用的可用的內存地址,所有在“棧”上的該方法所使用內存都被清空,且程序將自動回到“棧“上最初的方法調用的位置。

 

 

下面我們來看看,值類型的變量分配到“堆”上到情況。

public class MyInt
{
    public int MyValue;
}

public MyInt AddFive(int pValue)
{
    var result = new MyInt();
    result.MyValue = pValue + 5;
    return result;
}

和前面一樣,線程開始執行函數,函數參數被壓入線程堆棧。

由於 MyInt 為引用類型,它被分配在“堆”上,並且由一個位於“棧”上的指針引用。

AddFive()函數執行完畢后,“棧”同樣會被清空。

 

最后,只剩下一個 MyInt 類被留在“堆”上(“棧”上再也沒有指向這個 MyInt 類的指針)!這個時候就需要GC機制來處理了。

 

好了,大家對“棧”和“堆”有一定的了解之后,下面讓我們來稍微深入一點,來看兩個例子。

public int ReturnValue()
{
    int x = 3; 
    int y = x;
    y = 4;
    return x;
}

返回值大家其實已經知道了吧,還是3。這是為什么呢?這是因為,值類型,使用的就是值本身,其實在做int y = x的時候,就把3做了一份“拷貝”賦值給了y,然后在y = 4的時候,操作的是這個“拷貝”,並不會操作原來的3。

接下來我們看另外一個例子,MyInt還是使用上面例子中使用過的MyInt類。

public int ReturnValue()
{
    var x = new MyInt();
    x.MyValue = 3; 
    MyInt y; 
    y = x;
    y.MyValue = 4; 
    return x.MyValue;
}

在這個代碼中,x.MyValue是否還是會是3呢?是否y.MyValue還是操作的3的“拷貝”呢?其實只要運行一下這個代碼,就可以得出結果,x.MyValue還是被修改成了4。這是因為,當我們使用引用類型時,我們實際上是在使用指向這些對象的地址,而不是直接使用這些對象本身。

 

好了,暫時就介紹到這里,如果有什么疑問,或者有誤的地方,歡迎大家指點和交流。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM