首先,讓咱們來簡單瞭解一下什麼是「棧」(stack),什麼是「堆」(heap)。「棧」其實就是一種後入先出(LIFO)的數據結構。在咱們.NET Framework裏面,由CLR負責管理,咱們程序員不用去擔憂垃圾回收的問題;每個線程都有本身的專屬的「棧」。「堆」的存放就要零散一些,而且由 Garbage Collector(GC)執行管理,咱們關注的垃圾回收部分,就是在「堆」上的垃圾回收;其次就是整個進程共用一個「堆」。程序員
咱們先來記住兩條黃金法則:數據結構
1.引用類型老是被分配到「堆」上。函數
2.值類型老是分配到它聲明的地方:spa
a.做爲引用類型的成員變量分配到「堆」上線程
b.做爲方法的局部變量時分配到「棧」上指針
要真正理解上面的兩條黃金法則,還須要瞭解一下,「棧」和「堆」是如何工做的。首先如下面的這個方法爲例:code
public int AddFive(int pValue) { int result; result = pValue + 5; return result; }
1.方法AddFive()被壓入「棧」對象
2.緊接着方法參數pValue被壓入「棧」blog
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。這是由於,當咱們使用引用類型時,咱們其實是在使用指向這些對象的地址,而不是直接使用這些對象自己。
好了,暫時就介紹到這裏,若是有什麼疑問,或者有誤的地方,歡迎你們指點和交流。