這個操作發生在棧的頂部,請注意我們看到已經有很多成員之前被壓入到棧中了。首先是方法的本身先被壓入棧中,緊接著是參數入棧。然後是通過AddFive()裡面的指令來執行函數。函數執行的結果同樣也需要分配一些內存來存放,而這些內存也分配在棧中。函數執行結束後,就要將結果返回。最後,通過刪除AddFive()的指針來清除所有之前棧中有關於函數運行時分配的內存。並繼續下一個函數(可能之前就存在在棧中)。在這個例子中,我們的結果存儲在棧中。事實上,所有函數體內的值類型聲明都會分配到棧中。但是現在有些值類型也被分配在堆中。記住一個規則,值類型總是出現在聲明它們的地方。如果一個值類型聲明在函數體外,但是存於一個引用類型內,那麼它將跟這個引用類型一樣位於堆中。這裡用另外的一個例子來說明這個問題:
public class MyInt{
public int MyValue;
}
public MyInt AddFive(int pValue){
MyInt result = new MyInt();
result.MyValue = pValue + 5;
return result;
}
現在這個函數的執行跟先前的有了點不同。這裡的函數返回是一個MyInt類對象,也就是說是一個引用類型。引用類型是被分配在堆中的,而引用的指針是分配在棧中。
在AddFive()函數執行結束後,我們將清理棧中的內存。
在這裡我們看到除了棧中有數據,在堆中也有一些數據。而堆中的數據將被垃圾回收器回收。當我們的程序需要一塊內存並且已經沒有空閒的內存可以分配時,垃圾回收器開始運行。垃圾回收器會先停止所有運行中的線程,掃描堆中的所有對象並刪除那些沒有被主程序訪問的對象。垃圾回收器將重新組織堆中的所有空閒的空間,並調整所有棧中和堆中的相關指針。就像你能想到的那樣,這樣的操作會非常的影響效率。因此這也是為什麼我們要強調編寫高性能的代碼。好,那我要怎麼樣去做呢?
當我們在操作一個引用類型的時候,我們操作的是它的指針而不是它本身。當我們使用值類型的時候我們使用的是它本身,這個很明顯。我們看一下代碼:
public int ReturnValue() {
int x = new int();
x = 3;
int y = new int();
y = x;
y = 4;
return x;
}
這段代碼很簡單,返回3。但是如果我們改用引用類型MyInt類,結果可能不同:
public class MyInt {
public int MyValue;
}
public int ReturnValue2() {
MyInt x = new MyInt();
x.MyValue = 3;
MyInt y = new MyInt();
y = x;
y.MyValue = 4;
return x.MyValue;
}
這裡的返回值卻是4。為什麼呢? 想象一下,我們之前講的內容,我們在操作值類型數據的時候只是操作該值的一個副本。而在操作引用類型數據的時候,我們操作的是該類型的指針,所以y = x就修改了y的指針內容,從而使得y也指向了x那一部分棧空間。所以y.MyValue = 4 => x.MyValue = 4。所以返回值會是4 。