一、內(nèi)存基本構(gòu)成
可編程內(nèi)存在基本上分為這樣的幾大部分:靜態(tài)存儲(chǔ)區(qū)、堆區(qū)和棧區(qū)。他們的功能不同,對(duì)他們使用方式也就不同。
堆區(qū):亦稱(chēng)動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用malloc或new申請(qǐng)任意大小的內(nèi)存,程序員自己負(fù)責(zé)在適當(dāng)?shù)臅r(shí)候用free或delete釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期可以由我們決定,如果某動(dòng)態(tài)內(nèi)存不再使用,需要將其釋放掉,否則就會(huì)發(fā)生內(nèi)存泄漏現(xiàn)象。
(OC中對(duì)象存儲(chǔ)于堆中,當(dāng)對(duì)象的應(yīng)用計(jì)數(shù)為0時(shí)自動(dòng)釋放該對(duì)象)棧區(qū):在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都可以在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。
(OC中非對(duì)象的變量都存在棧中)靜態(tài)存儲(chǔ)區(qū):內(nèi)存在程序編譯的時(shí)候就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。它主要存放靜態(tài)數(shù)據(jù)、全局?jǐn)?shù)據(jù)和常量。
二、使用時(shí)需注意的規(guī)則
-
【規(guī)則1】用malloc 或new申請(qǐng)內(nèi)存之后,應(yīng)該立即檢查指針值是否為NULL。防止使用指針值為NULL的內(nèi)存。 -
【規(guī)則2】不要忘記為數(shù)組和動(dòng)態(tài)內(nèi)存賦初值。防止將未被初始化的內(nèi)存作為右值使用。 -
【規(guī)則3】避免數(shù)組或指針的下標(biāo)越界,特別要當(dāng)心發(fā)生“多1”或者“少1”操作。 -
【規(guī)則4】動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放必須配對(duì),防止內(nèi)存泄漏。 -
【規(guī)則5】用free 或delete 釋放了內(nèi)存之后,立即將指針設(shè)置為NULL,防止產(chǎn)生野指針。
三、堆與棧的討論
1、管理方式:
- 堆中資源由程序員控制(容易產(chǎn)生
memory leak), - 棧中資源由
編譯器自動(dòng)管理,無(wú)需手工控制。
2、系統(tǒng)響應(yīng):
- 對(duì)于堆,應(yīng)知道系統(tǒng)有一個(gè)記錄空閑
內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序申請(qǐng)時(shí),遍歷該鏈表,尋找第一個(gè)空間大于申請(qǐng)空間的堆結(jié)點(diǎn),刪除空閑結(jié)點(diǎn)鏈表中的該結(jié)點(diǎn),并將該結(jié)點(diǎn)空間分配給程序(大多數(shù)系統(tǒng)會(huì)在這塊內(nèi)存空間首地址記錄本次分配的大小,這樣delete才能正確釋放本內(nèi)存空間,另外系統(tǒng)會(huì)將多余的部分重新放入空閑鏈表中)。 - 對(duì)于棧,只要棧的
剩余空間大于所申請(qǐng)空間,系統(tǒng)為程序提供內(nèi)存,否則報(bào)異常提示棧溢出。
3、空間大?。?/h6>
- 堆是
不連續(xù)的內(nèi)存區(qū)域(因?yàn)橄到y(tǒng)是用鏈表來(lái)存儲(chǔ)空閑內(nèi)存地址),堆大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存(32bit系統(tǒng)理論上是4G),所以堆的空間比較靈活,比較大。
- 棧是一塊
連續(xù)的內(nèi)存區(qū)域,大小是操作系統(tǒng)預(yù)定好的,windows下棧大小是2M(也有是1M,在編譯時(shí)確定,VC中可設(shè)置)。
4、碎片問(wèn)題:
- 對(duì)于堆,頻繁的
new/delete會(huì)造成大量碎片,使程序效率降低。
- 對(duì)于棧,它是一個(gè)
先進(jìn)后出的隊(duì)列,進(jìn)出一一對(duì)應(yīng),不會(huì)產(chǎn)生碎片。
5、生長(zhǎng)方向:
- 堆向上,向
高地址方向增長(zhǎng)。
- 棧向下,向
低地址方向增長(zhǎng)。
6、分配方式:
- 堆都是
動(dòng)態(tài)分配(沒(méi)有靜態(tài)分配的堆)。
- 棧有
靜態(tài)分配和動(dòng)態(tài)分配,
靜態(tài)分配由編譯器完成(如局部變量分配),
動(dòng)態(tài)分配由alloca函數(shù)分配,但棧的動(dòng)態(tài)分配的資源由編譯器進(jìn)行釋放,無(wú)需程序員實(shí)現(xiàn)。
7、分配效率:
- 堆由
C/C++函數(shù)庫(kù)提供,機(jī)制很復(fù)雜。所以堆的效率比棧低很多。
- 棧是基于系統(tǒng)提供的
數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)在底層對(duì)棧提供支持,分配專(zhuān)門(mén)寄存器存放棧地址,棧操作有專(zhuān)門(mén)指令
四、為什么要把堆和棧區(qū)分出來(lái)
1、從軟件設(shè)計(jì)的角度看,棧代表了處理邏輯,而堆代表了數(shù)據(jù)。這種隔離、模塊化的思想,使得處理邏輯更為清晰
2、使得堆中的內(nèi)容可以被多個(gè)棧共享(也可以理解為多個(gè)線程訪問(wèn)同一個(gè)對(duì)象)
- 一方面這種
共享提供了一種有效的數(shù)據(jù)交互方式(如:共享內(nèi)存)
- 另一方面,堆中的共享常量和緩存可以被所有棧訪問(wèn),節(jié)省了空間
3、使得動(dòng)態(tài)增長(zhǎng)成為可能,相應(yīng)棧中只需記錄堆中的一個(gè)地址即可
不連續(xù)的內(nèi)存區(qū)域(因?yàn)橄到y(tǒng)是用鏈表來(lái)存儲(chǔ)空閑內(nèi)存地址),堆大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存(32bit系統(tǒng)理論上是4G),所以堆的空間比較靈活,比較大。連續(xù)的內(nèi)存區(qū)域,大小是操作系統(tǒng)預(yù)定好的,windows下棧大小是2M(也有是1M,在編譯時(shí)確定,VC中可設(shè)置)。new/delete會(huì)造成大量碎片,使程序效率降低。先進(jìn)后出的隊(duì)列,進(jìn)出一一對(duì)應(yīng),不會(huì)產(chǎn)生碎片。高地址方向增長(zhǎng)。低地址方向增長(zhǎng)。動(dòng)態(tài)分配(沒(méi)有靜態(tài)分配的堆)。靜態(tài)分配和動(dòng)態(tài)分配,靜態(tài)分配由編譯器完成(如局部變量分配),
動(dòng)態(tài)分配由
alloca函數(shù)分配,但棧的動(dòng)態(tài)分配的資源由編譯器進(jìn)行釋放,無(wú)需程序員實(shí)現(xiàn)。C/C++函數(shù)庫(kù)提供,機(jī)制很復(fù)雜。所以堆的效率比棧低很多。數(shù)據(jù)結(jié)構(gòu),計(jì)算機(jī)在底層對(duì)棧提供支持,分配專(zhuān)門(mén)寄存器存放棧地址,棧操作有專(zhuān)門(mén)指令
四、為什么要把堆和棧區(qū)分出來(lái)
軟件設(shè)計(jì)的角度看,棧代表了處理邏輯,而堆代表了數(shù)據(jù)。這種隔離、模塊化的思想,使得處理邏輯更為清晰堆中的內(nèi)容可以被多個(gè)棧共享(也可以理解為多個(gè)線程訪問(wèn)同一個(gè)對(duì)象)共享提供了一種有效的數(shù)據(jù)交互方式(如:共享內(nèi)存)動(dòng)態(tài)增長(zhǎng)成為可能,相應(yīng)棧中只需記錄堆中的一個(gè)地址即可棧因?yàn)檫\(yùn)行時(shí)的需要,比如保存系統(tǒng)運(yùn)行的上下文,需要進(jìn)行地址段的劃分。由于棧只能向上增長(zhǎng),因此就會(huì)限制住棧存儲(chǔ)內(nèi)容的能力。而堆不同,堆中的對(duì)象是可以根據(jù)需要?jiǎng)討B(tài)增長(zhǎng)的
4、面向?qū)ο?/code>就是堆和棧的完美結(jié)合。
對(duì)象的屬性其實(shí)就是數(shù)據(jù),存放在堆中;而對(duì)象的行為(方法),就是運(yùn)行邏輯,放在棧中。我們?cè)诰帉?xiě)對(duì)象的時(shí)候,其實(shí)即編寫(xiě)了數(shù)據(jù)結(jié)構(gòu),也編寫(xiě)的處理數(shù)據(jù)的邏輯