Memory (Heap v.s. Stack)
現代電腦系統大多依照Von Neumann Architecture設計而成,其中一個特色stored programming就是指『程式執行一定要將欲執行的指令跟資料放入記憶體方可執行』。但是記憶體中又有再分成幾種不同的記憶體,像heap跟stack就是其中的重點。
程式執行過程主要分成三大區塊:global、stack、heap三塊。其中global區塊最最易理解,主要存放全域變數或宣告為static的靜態變數在此就不多做贅述;另外兩個區塊分別為stack跟heap,接下來分別說明。
Stack
Stack Memory Space主要是負責自動化管理記憶體的區塊。
在記憶體中不外乎就是要存放變數、函式相關資訊等資料,使運作過程可以順利取得所需的變數或函式所在地。要讓系統可以全自動化管理,代表需可被預期此變數或函數資訊的生命週期,一旦完全可預測代表可以安心的交由系統管理,這些資訊也在執行過程中都被存放在stack空間。
Stack中常見的存放資訊如下:區域變數(local variable)、函式參數(function/method parameter)、函數的返回位址(function/method return address)等資訊。
為何上述資訊會放於stack之中,簡單來說:
void method1() {
int x = 100;
}
上述的int x = 100,系統會在stack中找一個區塊給x,另外裡面的內容為100。然而,x會被放入stack主要是因為在編譯時期系統已經可以預知x從何時開始配置跟何時結束回收(當然就是看所屬block結束就跟著回收),由於配置跟回收的規則明確,當然就往stack擺囉。
再舉一例子:
void method2() {
method1();
}
上述當呼叫method1()時,系統會先把method2的返回位址存到stack當中,為何是存放在stack呢,因為函式的呼叫有後進先出的概念,當method1()被呼叫而開始執行,待結束時必定會查找該返回何處,故最後一定會讀取函式的返回位址,既然如此明確而有條理,當然也是往stack放。
Heap
在程式中,有部分的需求總是在執行中依據實際情況才會動態增減,這些資訊是難以被預測哪時候開始有?量有多少?何時該回收?…這些不可預測的因素造成上述的stack區塊不適合運用於此。當資訊為動態配置產生,系統會存放在另外一塊空間,稱之為『Heap』(注意這裡的Heap跟資料結構中的Heap不相關,可別會錯意!)。Heap的區塊專收執行期間動態產生的資料,由於為動態產生故結束點無法由系統來掌握,故需使用者自行回收空間。在C++或Java中利用new語法產生的就是動態配置的物件,需存放於heap中。
奇怪跑越久記憶體用越多的怪現象。許多時候執行的程式都沒有改變,但卻常出現隨時間執行越久程式所耗用的空間將越多,最後造成out of memory。工程師也不知為何如此,就是定期在out of memory之前restart程式即可。這中現象層出不窮,一般大多是因為工程師沒有正確將記憶體回收所導致。Heap中的資料如果沒有正常的回收,將會逐步成長到將記憶體消耗殆盡,下次發生上述問題的實後,切記自己檢查一下heap空間的資料有無正常回收。