在iOS中,內(nèi)存主要分為棧區(qū)、堆區(qū)、全局區(qū)、常量區(qū)、代碼區(qū)五大區(qū)域。如下圖所示

下面分別介紹這五大區(qū)
棧區(qū)(Stack)
定義
棧是
系統(tǒng)數(shù)據(jù)結(jié)構(gòu),其對(duì)應(yīng)的進(jìn)程或者線程是唯一的棧是
向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)棧是一塊
連續(xù)的內(nèi)存區(qū)域,遵循先進(jìn)后出(FILO)原則棧的
地址空間在iOS中是以0X7開(kāi)頭棧區(qū)一般在
運(yùn)行時(shí)分配
存儲(chǔ)
棧區(qū)是由編譯器自動(dòng)分配并釋放的,主要用來(lái)存儲(chǔ)
局部變量函數(shù)的參數(shù),例如函數(shù)的隱藏參數(shù)(id self,SEL _cmd)
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):因?yàn)闂J怯?code>編譯器自動(dòng)分配并釋放的,不會(huì)產(chǎn)生內(nèi)存碎片,所以
快速高效-
缺點(diǎn):棧的
內(nèi)存大小有限制,數(shù)據(jù)不靈活iOS主線程棧大小是1MB其他線程是
512KBMAC只有8M
以上內(nèi)存大小的說(shuō)明,在Threading Programming Guide中有相關(guān)說(shuō)明

堆區(qū)(Heap)
定義
堆是
向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)堆是
不連續(xù)的內(nèi)存區(qū)域,類似于鏈表結(jié)構(gòu)(便于增刪,不便于查詢),遵循先進(jìn)先出(FIFO)原則堆的
地址空間在iOS中是以0x6開(kāi)頭,其空間的分配總是動(dòng)態(tài)的堆區(qū)的分配一般是在
運(yùn)行時(shí)分配
存儲(chǔ)
堆區(qū)是由程序員動(dòng)態(tài)分配和釋放的,如果程序員不釋放,程序結(jié)束后,可能由操作系統(tǒng)回收,主要用于存放
OC中使用alloc或者 使用new開(kāi)辟空間創(chuàng)建對(duì)象C語(yǔ)言中使用malloc、calloc、realloc分配的空間,需要free釋放
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):靈活方便,數(shù)據(jù)適應(yīng)面廣泛
缺點(diǎn):需
手動(dòng)管理,速度慢、容易產(chǎn)生內(nèi)存碎片
當(dāng)需要訪問(wèn)堆中內(nèi)存時(shí),一般需要先通過(guò)對(duì)象讀取到棧區(qū)的指針地址,然后通過(guò)指針地址訪問(wèn)堆區(qū)
全局區(qū)(靜態(tài)區(qū),即.bss & .data)
全局區(qū)是編譯時(shí)分配的內(nèi)存空間,在iOS中一般以0x1開(kāi)頭,在程序運(yùn)行過(guò)程中,此內(nèi)存中的數(shù)據(jù)一直存在,程序結(jié)束后由系統(tǒng)釋放,主要存放
未初始化的全局變量和靜態(tài)變量,即BSS區(qū)(.bss)已初始化的全局變量和靜態(tài)變量,即數(shù)據(jù)區(qū)(.data)
其中,全局變量是指變量值可以在運(yùn)行時(shí)被動(dòng)態(tài)修改,而靜態(tài)變量是static修飾的變量,包含靜態(tài)局部變量和靜態(tài)全局變量
常量區(qū)(即.rodata)
常量區(qū)是編譯時(shí)分配的內(nèi)存空間,在程序結(jié)束后由系統(tǒng)釋放,主要存放
- 已經(jīng)使用了的,且沒(méi)有指向的
字符串常量
字符串常量因?yàn)榭赡茉诔绦蛑斜欢啻问褂?,所以`在程序運(yùn)行之前就會(huì)提前分配內(nèi)存
代碼區(qū)(即.text)
代碼區(qū)是編譯時(shí)分配主要用于存放程序運(yùn)行時(shí)的代碼,代碼會(huì)被編譯成二進(jìn)制存進(jìn)內(nèi)存的
內(nèi)存五大區(qū)驗(yàn)證
運(yùn)行下面一段代碼,看看變量在內(nèi)存中是如何分配的
- (void)test{
NSInteger i = 123;
NSLog(@"i的內(nèi)存地址:%p", &i);
NSString *string = @"CJL";
NSLog(@"string的內(nèi)存地址:%p", string);
NSLog(@"&string的內(nèi)存地址:%p", &string);
NSObject *obj = [[NSObject alloc] init];
NSLog(@"obj的內(nèi)存地址:%p", obj);
NSLog(@"&obj的內(nèi)存地址:%p", &obj);
}
運(yùn)行結(jié)果如下

- 對(duì)于
局部變量i,從地址可以看出是0x7開(kāi)頭,所以i存放在棧區(qū) - 對(duì)于
字符串對(duì)象string,分別打印了string的對(duì)象地址和string對(duì)象的指針地址string的
對(duì)象地址是以0x1開(kāi)頭,說(shuō)明是存放在常量區(qū)string
對(duì)象的指針地址是以0x7開(kāi)頭,說(shuō)明是存放在棧區(qū)
- 對(duì)于
alloc創(chuàng)建的對(duì)象obj,分別打印了obj的對(duì)象地址和obj對(duì)象的指針地址(可以參考前文的匯總圖)obj的
對(duì)象地址是以0x6開(kāi)頭,說(shuō)明是存放在堆區(qū)obj
對(duì)象的指針地址是以0x7開(kāi)頭,說(shuō)明是存放在棧區(qū)
函數(shù)棧
函數(shù)棧又稱為棧區(qū),在內(nèi)存中從高地址往低地址分配,與堆區(qū)相對(duì),具體圖示請(qǐng)查看文章最開(kāi)始的圖示棧幀是指函數(shù)(運(yùn)行中且未完成)占用的一塊獨(dú)立的連續(xù)內(nèi)存區(qū)域應(yīng)用中新創(chuàng)建的
每個(gè)線程都有專用的??臻g,??梢栽诰€程期間自由使用。而線程中有千千萬(wàn)萬(wàn)的函數(shù)調(diào)用,這些函數(shù)共享進(jìn)程的這個(gè)棧空間。每個(gè)函數(shù)所使用的棧空間是一個(gè)棧幀,所有的棧幀就組成了這個(gè)線程完整的棧函數(shù)調(diào)用是發(fā)生在棧上的,每個(gè)函數(shù)的相關(guān)信息(例如局部變量、調(diào)用記錄等)都存儲(chǔ)在一個(gè)棧幀中,每執(zhí)行一次函數(shù)調(diào)用,就會(huì)生成一個(gè)與其相關(guān)的棧幀,然后將其棧幀壓入函數(shù)棧,而當(dāng)函數(shù)執(zhí)行結(jié)束,則將此函數(shù)對(duì)應(yīng)的棧幀出棧并釋放掉
如下圖所示,是經(jīng)典圖 - ARM的棧幀布局方式

其中
main stack frame為調(diào)用函數(shù)的棧幀func1 stack frame為當(dāng)前函數(shù)(被調(diào)用者)的棧幀棧底在高地址,棧向下增長(zhǎng)。FP就是棧基址,它指向函數(shù)的棧幀起始地址SP則是函數(shù)的棧指針,它指向棧頂的位置。ARM壓棧的順序很是規(guī)矩(也比較容易被黑客攻破么),依次為當(dāng)前函數(shù)指針PC、返回指針LR、棧指針SP、?;稦P、傳入?yún)?shù)個(gè)數(shù)及指針、本地變量和臨時(shí)變量。如果函數(shù)準(zhǔn)備調(diào)用另一個(gè)函數(shù),跳轉(zhuǎn)之前臨時(shí)變量區(qū)先要保存另一個(gè)函數(shù)的參數(shù)。ARM也可以
用?;泛蜅V羔樏鞔_標(biāo)示棧幀的位置,棧指針SP一直移動(dòng),ARM的特點(diǎn)是,兩個(gè)??臻g內(nèi)的地址(SP+FP)前面,必然有兩個(gè)代碼地址(PC+LR)明確標(biāo)示著調(diào)用函數(shù)位置內(nèi)的某個(gè)地址。
堆棧溢出
一般情況下應(yīng)用程序是不需要考慮堆和棧的大小的,但是事實(shí)上堆和棧都不是無(wú)上限的,過(guò)多的遞歸會(huì)導(dǎo)致棧溢出,過(guò)多的alloc變量會(huì)導(dǎo)致堆溢出。
所以預(yù)防堆棧溢出的方法:
(1)避免層次過(guò)深的遞歸調(diào)用;
(2)不要使用過(guò)多的局部變量,控制局部變量的大??;
(3)避免分配占用空間太大的對(duì)象,并及時(shí)釋放;
(4)實(shí)在不行,適當(dāng)?shù)那榫跋?code>調(diào)用系統(tǒng)API修改線程的堆棧大小;
棧幀示例
描述下面代碼的棧幀變化
棧幀程序示例
int Add(int x,int y) {
int z = 0;
z = x + y;
return z;
}
int main() {
int a = 10;
int b = 20;
int ret = Add(a, b);
}
程序執(zhí)行時(shí)棧區(qū)中棧幀的變化如下圖所示
