棧(Stack)和堆(Heap)。
棧是在程序運行過程中用于保存指令,值類型變量的內存區域(一個線程對應一個棧),棧的結構和數據結構中“棧”的結構是一樣的,“先進后出”。
堆也就是托管堆(managed heap),進程初始化的時候,CLR要保留一塊連續的地址空間,這個地址空間最初并沒有對應的物理存儲空間。這個地址空間就是托管堆。
* 棧vs堆:有什么不同?
棧負責保存我們的代碼執行(或調用)路徑,而堆則負責保存對象(或者說數據,接下來將談到很多關于堆的問題)的路徑。
可以將棧想象成一堆從頂向下堆疊的盒子。當每調用一次方法時,我們將應用程序中所要發生的事情記錄在棧頂的一個盒子中,而我們每次只能夠使用棧頂的那個盒子。當我們棧頂的盒子被使用完之后,或者說方法執行完畢之后,我們將拋開這個盒子然后繼續使用棧頂上的新盒子。堆的工作原理比較相似,但大多數時候堆用作保存信息而非保存執行路徑,因此堆能夠在任意時間被訪問。與棧相比堆沒有任何訪問限制,堆就像床上的舊衣服,我們并沒有花時間去整理,那是因為可以隨時找到一件我們需要的衣服,而棧就像儲物柜里堆疊的鞋盒,我們只能從最頂層的盒子開始取,直到發現那只合適的。
棧是自行維護的,也就是說內存自動維護棧,當棧頂的盒子不再被使用,它將被拋出。相反的,堆需要考慮垃圾回收,垃圾回收用于保持堆的整潔性,沒有人愿意看到周圍都是贓衣服,那簡直太臭了!
* 棧和堆里有些什么?
當我們的代碼執行的時候,棧和堆中主要放置了四種類型的數據:值類型(Value Type),引用類型(Reference Type),指針(Pointer),指令(Instruction)。
1.?引用類型總是放在堆中。
2.?值類型和指針總是放在它們被聲明的地方。
* 參數,大問題
這里有一個代碼執行時的詳細介紹,我們將深入第一部分出現的方法調用過程...
當我們調用一個方法時,會發生以下的事情:
1.方法執行時,首先在棧上為對象實例中的方法分配空間,然后將方法拷貝到棧上(此時的棧被稱為幀),但是該空間中只存放了執行方法的指令,并沒有方法內的數據項。
2.方法的調用地址(或者說指針)被放置到棧上,一般來說是一個GOTO指令,使我們能夠在方法執行完成之后,知道回到哪個地方繼續執行程序。(最好能理解這一點,但并不是必須的,因為這并不會影響我們的編碼)
3.方法參數的分配和拷貝是需要空間的,這一點是我們需要進一步注意。
4.控制此時被傳遞到了幀上,然后線程開始執行我們的代碼。因此有另一個方法叫做"調用棧"。
* 值類型傳遞
深拷貝
首先,當我們傳遞一個值類型參數時,棧上被分配好一個新的空間,然后該參數的值被拷貝到此空間中。
必須注意的是,如果我們要將一個非常大的值類型數據(如數據量大的struct類型)入棧,它會占用非常大的內存空間,而且會占有過多的處理器周期來進行拷貝復制。棧并沒有無窮無盡的空間,它就像在水龍頭下盛水的杯子,隨時可能溢出。struct是一個能夠存放大量數據的值類型成員,我們必須小心地使用。
* 引用類型傳遞
淺拷貝