堆與棧是操作系統(tǒng)對進(jìn)程占用的內(nèi)存空間的兩種管理方式哪自,主要有如下幾種區(qū)別:
(1)管理方式不同。棧由操作系統(tǒng)自動分配釋放禁熏,無需我們手動控制壤巷;堆的申請和釋放工作由程序員控制,容易產(chǎn)生內(nèi)存泄漏瞧毙;
(2)空間大小不同胧华。每個進(jìn)程擁有的棧的大小要遠(yuǎn)遠(yuǎn)小于堆的大小。理論上宙彪,程序員可申請的堆大小為虛擬內(nèi)存的大小矩动,進(jìn)程棧的大小 64bits 的 Windows 默認(rèn) 1MB,64bits 的 Linux 默認(rèn) 10MB您访;
(3)生長方向不同铅忿。堆的生長方向向上,內(nèi)存地址由低到高灵汪;棧的生長方向向下檀训,內(nèi)存地址由高到低。
(4)分配方式不同享言。堆都是動態(tài)分配的峻凫,沒有靜態(tài)分配的堆。棧有2種分配方式:靜態(tài)分配和動態(tài)分配览露。靜態(tài)分配是由操作系統(tǒng)完成的荧琼,比如局部變量的分配。動態(tài)分配由alloca函數(shù)進(jìn)行分配差牛,但是棧的動態(tài)分配和堆是不同的命锄,他的動態(tài)分配是由操作系統(tǒng)進(jìn)行釋放,無需我們手工實現(xiàn)偏化。
(5)分配效率不同脐恩。棧由操作系統(tǒng)自動分配,會在硬件層級對棧提供支持:分配專門的寄存器存放棧的地址侦讨,壓棧出棧都有專門的指令執(zhí)行驶冒,這就決定了棧的效率比較高。堆則是由C/C++提供的庫函數(shù)或運(yùn)算符來完成申請與管理韵卤,實現(xiàn)機(jī)制較為復(fù)雜骗污,頻繁的內(nèi)存申請容易產(chǎn)生內(nèi)存碎片。顯然沈条,堆的效率比棧要低得多需忿。
(6)存放內(nèi)容不同。棧存放的內(nèi)容,函數(shù)返回地址贴谎、相關(guān)參數(shù)汞扎、局部變量和寄存器內(nèi)容等。當(dāng)主函數(shù)調(diào)用另外一個函數(shù)的時候擅这,要對當(dāng)前函數(shù)執(zhí)行斷點(diǎn)進(jìn)行保存澈魄,需要使用棧來實現(xiàn),首先入棧的是主函數(shù)下一條語句的地址仲翎,即擴(kuò)展指針寄存器的內(nèi)容(EIP)痹扇,然后是當(dāng)前棧幀的底部地址,即擴(kuò)展基址指針寄存器內(nèi)容(EBP)溯香,再然后是被調(diào)函數(shù)的實參等鲫构,一般情況下是按照從右向左的順序入棧桥胞,之后是被調(diào)函數(shù)的局部變量省核,注意靜態(tài)變量是存放在數(shù)據(jù)段或者BSS段,是不入棧的尝艘。出棧的順序正好相反湿镀,最終棧頂指向主函數(shù)下一條語句的地址炕吸,主程序又從該地址開始執(zhí)行。堆勉痴,一般情況堆頂使用一個字節(jié)的空間來存放堆的大小赫模,而堆中具體存放內(nèi)容是由程序員來填充的。
從以上可以看到蒸矛,堆和棧相比瀑罗,由于大量malloc()/free()或new/delete的使用,容易造成大量的內(nèi)存碎片雏掠,并且可能引發(fā)用戶態(tài)和核心態(tài)的切換斩祭,效率較低。棧相比于堆乡话,在程序中應(yīng)用較為廣泛摧玫,最常見的是函數(shù)的調(diào)用過程由棧來實現(xiàn),函數(shù)返回地址蚊伞、EBP席赂、實參和局部變量都采用棧的方式存放吮铭。雖然棧有眾多的好處时迫,但是由于和堆相比不是那么靈活,有時候分配大量的內(nèi)存空間谓晌,主要還是用堆掠拳。
無論是堆還是棧,在內(nèi)存使用時都要防止非法越界纸肉,越界導(dǎo)致的非法內(nèi)存訪問可能會摧毀程序的堆溺欧、棧數(shù)據(jù)喊熟,輕則導(dǎo)致程序運(yùn)行處于不確定狀態(tài),獲取不到預(yù)期結(jié)果姐刁,重則導(dǎo)致程序異常崩潰芥牌,這些都是我們編程時與內(nèi)存打交道時應(yīng)該注意的問題。
C語言的內(nèi)存模型分為5個區(qū):棧區(qū)聂使、堆區(qū)壁拉、靜態(tài)區(qū)、常量區(qū)柏靶、代碼區(qū)弃理。每個區(qū)存儲的內(nèi)容如下:
1、棧區(qū):存放函數(shù)的參數(shù)值屎蜓、局部變量等痘昌,由編譯器自動分配和釋放,通常在函數(shù)執(zhí)行完后就釋放了炬转,其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧辆苔。棧內(nèi)存分配運(yùn)算內(nèi)置于CPU的指令集,效率很高返吻,但是分配的內(nèi)存量有限姑子,比如iOS中棧區(qū)的大小是2M。
2测僵、堆區(qū):就是通過new街佑、malloc、realloc分配的內(nèi)存塊捍靠,編譯器不會負(fù)責(zé)它們的釋放工作沐旨,需要用程序區(qū)釋放。分配方式類似于數(shù)據(jù)結(jié)構(gòu)中的鏈表榨婆。
3磁携、靜態(tài)區(qū):全局變量和靜態(tài)變量(在iOS中就是用static修飾的局部變量或者是全局全局變量)的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域良风,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域谊迄。程序結(jié)束后,由系統(tǒng)釋放烟央。
4统诺、常量區(qū):常量存儲在這里,不允許修改疑俭。
5粮呢、代碼區(qū):存放函數(shù)體的二進(jìn)制代碼。