http://patmusing.blog.163.com/blog/static/135834960201001512358686/程序員
|
棧(Stack)web |
堆(Heap)算法 |
申請方式數據結構 |
由OS自動分配。例如在函數聲明一個局部變量int b; OS自動在棧中爲b開闢空間函數 |
須要程序員本身申請,並指明大小,在c中malloc函數,如p1 = (char*)malloc(10);在C++中用new運算符如p2 = new char[10];spa 注意:p1和p2自己是在棧中的操作系統 |
申請後系統響應.net |
只要棧的剩餘空間大於所申請的空間,系統將爲程序提供內存,不然將報異常提示棧移除。3d |
首先應該知道操做系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,而後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,因爲找到的堆結點的大小不必定正好等於申請的大小,系統會自動的將多餘的那部分從新放入空閒鏈表中。指針 |
申請大小的限制 |
棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M (也有的說是1M,總之是一個編譯時就肯定的常數),若是申請的空間超過棧的剩餘空間時,將提示overflow。所以,能從棧得到的空間較小。 |
堆是向高地址擴展的數據結構,是不連續的內存區域。這是因爲系統是用鏈表來存儲的空閒內存地址的,天然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。因而可知,堆得到的空間比較靈活,也比較大。 |
申請的效率 |
棧由系統自動分配,速度較快。但程序員是沒法控制的。 |
堆是由new分配的內存,通常速度比較慢,並且容易產生內存碎片,不過用起來最方便。 另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,它不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。可是速度快,也最靈活。 |
存儲內容 |
在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址,而後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,而後是函數中的局部變量。注意靜態變量是不入棧的。 當本次函數調用結束後,局部變量先出棧,而後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的下一條指令,程序由該點繼續運行。 |
通常是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。 |
存取效率 |
快 |
慢 |
/***************************************************************************************************************************************/
棧(stack),就是那些由編譯器在須要的時候分配,在不須要的時候自動清除的變量的存儲區。裏面的變量一般是局部變量、函數參數等。
堆(heap),通常由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收。
全局/靜態存儲區,全局變量和靜態變量被分配到同一塊內存中,在之前的C語言中,全局變量又分爲初始化的(DATA段)和未初始化的(BSS段),在C++裏面沒有這個區分了,它們共同佔用同一塊內存區。
常量存儲區,常量字符串就是放在這裏的,不容許修改(經過非正當手段也能夠修改,並且方法不少),程序結束後由系統釋放。
|----------------------|
| 內核虛擬存儲器 |
|----------------------|
| 用戶棧(Statk) |
|----------------------|
| | |
| / / |
| |
| / / |
| | |
|----------------------|
| 堆(Heap) |
|----------------------|
| 未初始化(BSS) |
|----------------------|
| 初始化(Data) |
|----------------------|
| 正文段(Text |
|----------------------|
區分堆與棧:
1: void f() { int* p=new int[5]; }
這條短短的一句話就包含了堆與棧,關鍵new指示分配了一塊堆內存,而指針P分配的是一塊棧內存,因此這句話的意思就是:在棧內存中存放了一個指向一塊堆內存的指針p。在程序會先肯定在堆中分配內存的大小,而後調用operator new分配內存,而後返回這塊內存的首地址,放入棧中,他在VC6下的彙編代碼以下:
1: 00401028 push 14h2: 0040102A call operator new (00401060)3: 0040102F add esp,44: 00401032 mov dword ptr [ebp-8],eax5: 00401035 mov eax,dword ptr [ebp-8]6: 00401038 mov dword ptr [ebp-4],eax
堆與棧主要的區別由如下幾點:
1)管理方式:對於棧來說,是由編譯器自動管理,無需手動控制;對於堆來講,釋放工做由程序員控制,容易產生內存泄露。
2)申請後系統的響應:對於棧來說,只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,不然將報異常提示棧溢出。對於堆來說,首先應該知道操做系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,而後將該結點從空閒結點鏈表中刪除,並將剩餘的部分從新放入空閒鏈表中,最後將該結點的空間分配給程序。另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣代碼中的delete語句才能正確的釋放本內存空間。
3)空間大小:堆的大小受限於計算機系統中有效的虛擬內存。通常來說在32位系統下,堆內存能夠達到4G的空間,從這個角度來看堆內存幾乎是沒有什麼限制的。可是對於棧來說,通常都是有必定的空間大小的,例如,在VC6下面,默認的棧空間大小是1M。兩個參數均可以修改:Project->Setting->Link,在Category 中選中Output,而後在Reserve中設定堆棧的最大值和commit。
注意:Reserve最小值爲4Byte;commit是保留在虛擬內存的頁文件裏面,它設置的較大會使棧開闢較大的值,可能增長內存的開銷和啓動時間。
4)碎片問題:對於堆來說,頻繁的new/delete勢必會形成內存空間的不連續,從而形成大量的碎片,使程序效率下降。對於棧來說,則不會存在這個問題。
5)生長方向:對於堆來說,生長方向是向上的,也就是向着內存地址增長的方向;對於棧來說,它的生長方向是向下的,是向着內存地址減少的方向增加。(聯繫:小尾端是高位字節在高端地址、低位字節在低位地址,所以在壓棧時先壓高字節後壓低字節)
6)分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。堆則是C/C++函數庫提供的,它的機制是很複雜的,例如爲了分配一塊內存,庫函數會按照必定的算法,在堆內存中搜索可用的足夠大小的空間,若是沒有足夠大小的空間(多是因爲內存碎片太多),就有可能調用系統功能去增長程序數據段的內存空間,這樣就有機會分到足夠大小的內存,而後進行返回。顯然,堆的效率比棧要低得多。另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,它不是在堆,也不是在棧,而是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。可是速度快,也最靈活。
總之,堆和棧相比,因爲大量new/delete的使用,容易形成大量的內存碎片;因爲沒有專門的系統支持,效率很低;因爲可能引起用戶態和核心態的切換,內存的申請,代價變得更加昂貴。