操作系統中堆和棧的區別


操作系統中堆和棧的區別

 

可執行程序在存儲時(沒調入到內存)分為代碼區,數據區和未初始化數據去(bss)三部分。
1)代碼區:存放cpu執行的機器指令。一般代碼區可共享(另外的執行程序可調用它),因為對於頻繁被執行的程序,只需在內存中由一份代碼即可。並且,代碼區通常只讀,原因時防止程序意外修改其指令。還有代碼區還規划了局部變量相關信息。

2)全局初始化數據區/靜態數據區:包含了在程序中明確被初始化的全局變量、靜態變量(包括全局靜態變量和局部靜態變量)和常量數據。

比如一個不在任何函數內的聲明(全局數據):
int maxcount=99;
使得變量maxcount根據其初值被存儲到初始化數據區中。

又或者說是:
static mincount=100;
這聲明了一個靜態數據,如果是在函數體外聲明,則表示其為一個全局靜態變量,如果在函數體內,則表示其為一個局部靜態變量。另外,如果在函數名前加上static,則表示此函數只能在當前文件中被調用。

3)未初始化數據區(bbs):存入的是全局未初始化變量。BSS區的數據在程序開始執行之前被內核初始化為0或者空指針(NULL)。例如一個不在任何函數內的聲明:
long sum[1000];
將變量sum存儲到未初始化數據區。

下面的圖就是可執行代碼存儲時結構和運行時結構的對照圖,而一個正在運行着的C編譯程序占用的內存除了包含代碼區、初始化數據區、未初始化數據區,還有堆區和棧區:

1)代碼區:代碼區指令根據程序設計流程依次執行,對於順序指令,每個進程只會執行一次,如果反復,則需要使用跳轉指令,如果進行遞歸,則需要借助棧來實現。
代碼區的指令中包括操作碼和要操作的對象(或對象地址引用)。如果是立即數(即具體的數值,如5),將直接包含在代碼中;如果是局部數據,將在棧區分配空間,然后引用該數據地址;如果是BSS區和數據區,在代碼中同樣將引用該數據地址。
(2)全局初始化數據區/靜態數據區:只初始化一次。
(3)未初始化數據區(BSS):在運行時改變其值。
(4)棧區(stack):由編譯器自動分配釋放,存放函數的參數值、局部變量的值等。其操作方式類似於數據結構中的棧。每當一個函數被調用,該函數返回地址和一些關於調用的信息,比如某些寄存器的內容,被存儲到棧區。然后這個被調用的函數再為它的自動變量和臨時變量在棧區上分配空間,這就是C實現函數遞歸調用的方法。每執行一次遞歸函數調用,一個新的棧框架就會被使用,這樣這個新實例棧里的變量就不會和該函數的另一個實例棧里面的變量混淆。
(5)堆區(heap):用於動態內存分配。堆在內存中位於bss區和棧區之間。一般由程序員分配和釋放,若程序員不釋放,程序結束時有可能由OS回收。

分了這么5個區域來存放數據,主要有以下幾個考慮:
1)一個進程在運行過程中,代碼是根據流程依次執行的,只需訪問一次,不過跳轉和遞歸有可能使代碼執行多次。而數據一般都需要訪問多次,因此單獨開辟空間以方便訪問和節約空間。
2)臨時數據以及需再次使用的代碼在運行時放入棧區中,生命周期短。
3)全局數據和靜態數據有可能在整個程序執行過程中都需要訪問,因此單獨存儲管理。
4)堆區是由用戶自由分配的,以便管理。

下面通過一段簡單的代碼來查看C程序執行時的內存分配情況。相關數據在運行時的位置如注釋所述。

堆和棧的區別

前面已經講過,棧是由編譯器在需要時分配的,不需要時自動清除的變量存儲區。里面的變量通常是局部變量、函數參數等。堆是由malloc()函數(C++語言為new運算符)分配的內存塊,內存釋放由程序員手動控制,在C語言為free函數完成(C++中為delete)。

堆和棧的主要區別:

(1)管理方式不同。
棧編譯器自動管理,無需程序員手工控制;而堆空間的申請釋放工作由程序員控制,容易產生內存泄漏。

(2)空間大小不同。
棧是向低地址擴展的數據結構,是一塊連續的內存區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩余空間時,將提示溢出。因此,用戶能從棧獲得的空間較小。

堆是向高地址擴展的數據結構,是不連續的內存區域。因為系統是用鏈表來存儲空閑內存地址的,且鏈表的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。棧中元素都是一一對應的,不會存在一個內存塊從棧中間彈出的情況。

(3)是否產生碎片。
對於堆來講,頻繁的malloc/free(new/delete)勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低(雖然程序在退出后操作系統會對內存進行回收管理)。對於棧來講,則不會存在這個問題。

(4)增長方向不同。
堆的增長方向是向上的,即向着內存地址增加的方向;棧的增長方向是向下的,即向着內存地址減小的方向。

(5)分配方式不同。
堆都是程序中由malloc()函數動態申請分配並由free()函數釋放的;棧的分配和釋放是由編譯器完成的,棧的動態分配由alloca()函數完成,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實現。

(6)分配效率不同。
棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行。堆則是C函數庫提供的,它的機制很復雜,例如為了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考數據結構/操作系統)在堆內存中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由於內存碎片太多),就有需要操作系統來重新整理內存空間,這樣就有機會分到足夠大小的內存,然后返回。顯然,堆的效率比棧要低得多。

 

 

原文:http://blog.chinaunix.net/uid-23215128-id-2521223.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM