首先堆疊和堆(託管堆)都在程式的虛擬記憶體中。(在32位處理器上每個程式的虛擬記憶體為4GB)
堆疊stack
堆疊中儲存值型別。
堆疊實際上是向下填充,即由高記憶體地址指向地記憶體地址填充。
堆疊的工作方式是先分配記憶體的變數後釋放(先進後出原則)。
堆疊中的變數是從下向上釋放,這樣就保證了堆疊中先進後出的規則不與變數的生命週期起衝突!
堆疊的效能非常高,但是對於所有的變數來說還不太靈活,而且變數的生命週期必須巢狀。
通常我們希望使用一種方法分配記憶體來儲存資料,並且方法退出後很長一段時間內資料仍然可以使用。此時就要用到堆(託管堆)!
堆(託管堆)heap
堆(託管堆)儲存引用型別。
此堆非彼堆,.NET中的堆由垃圾收集器自動管理。
與堆疊不同,堆是從下往上分配,所以自由的空間都在已用空間的上面。
比如建立一個物件:
Customer cus;
cus = new Customer();
申明一個Customer的引用cus,在堆疊上給這個引用分配儲存空間。這僅僅只是一個引用,不是實際的Customer物件!
cus佔4個位元組的空間,包含了儲存Customer的引用地址。
接著分配堆上的記憶體以儲存Customer物件的例項,假定Customer物件的例項是32位元組,為了在堆上找到一個儲存Customer物件的儲存位置。
.NET執行庫在堆中搜尋第一個從未使用的,32位元組的連續塊儲存Customer物件的例項!
然後把分配給Customer物件例項的地址賦給cus變數!
從這個例子中可以看出,建立物件引用的過程比建立值變數的過程複雜,且不能避免效能的降低!
實際上就是.NET執行庫儲存對的狀態資訊,在堆中新增新資料時,堆疊中的引用變數也要更新。效能上損失很多!
有種機制在分配變數記憶體的時候,不會受到堆疊的限制:把一個引用變數的值賦給一個相同型別的變數,那麼這兩個變數就引用同一個堆中的物件。
當一個應用變數出作用域時,它會從堆疊中刪除。但引用物件的資料仍然保留在堆中,一直到程式結束 或者 該資料不被任何變數應用時,垃圾收集器會刪除它。