
- 如圖:內(nèi)存五大區(qū)由高到底分別為:桟區(qū)、堆區(qū)、全局區(qū)、常量區(qū)、代碼區(qū),全局區(qū)又分為
.bss(未初始化)、.data(初始化) - 內(nèi)核區(qū):主要是處理內(nèi)核模塊,比如我們的系統(tǒng)內(nèi)存為4GB,那么我們實際上能使用3GB,剩下的1GB就是給了內(nèi)核區(qū),指針地址0xc0000000(310241024*1024)
- 保留區(qū):用來給系統(tǒng)提供一些必要空間

桟 (Stack)
- 連續(xù)的內(nèi)存區(qū)域,大小一般在1M
- 在iOS中以
0x7開頭 - 主要存儲 局部變量,函數(shù)參數(shù),先進后出、一旦出了作用域就會被銷毀;函數(shù)跳轉(zhuǎn)地址,現(xiàn)場保護等;對象的指針放在桟區(qū)。
- 由編譯器自動分配并釋放,雖然不用我們?nèi)ス芾?,但是桟的大小也是限制的
堆區(qū) (Heap)
- 不連續(xù)的內(nèi)存區(qū)域,類似鏈表
- 在iOS中以
0x6開頭 - 我們自己創(chuàng)建的對象,需要自己釋放,需要經(jīng)常增加和刪除,所以放在堆區(qū)
創(chuàng)建好后需要經(jīng)常查找,所以對象的指針放在桟區(qū)。 - 當需要訪問堆中內(nèi)存時,堆中的所有東西都是匿名的,這樣不能按名字訪問,而-只能通過指針訪問,需要先通過對象讀取到棧區(qū)的指針地址,然后通過指針地址訪問堆區(qū),
- 對于堆來講,頻繁的new/delete勢必會造成內(nèi)存空間的不連續(xù)性,從而造成大量的碎片 ,使程序效率降低
全局區(qū)/靜態(tài)區(qū) (static)
- 在iOS中以
0x1開頭 - 包括兩個部分:
bss段( bss segment )、.data(data segment、數(shù)據(jù)段); -
.bss段通常是指用來存放程序中未初始化的全局變量和靜態(tài)變量的一塊內(nèi)存區(qū)域。 -
.data段通常是指用來存放程序中已經(jīng)初始化的全局變量和靜態(tài)變量的一塊內(nèi)存區(qū)域。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配,可以分為只讀數(shù)據(jù)段(常量區(qū))和讀寫數(shù)據(jù)段。字符串常量等,是放在只讀數(shù)據(jù)段中,結(jié)束程序時才會被收回。 - 也就是說,(全局區(qū)/靜態(tài)區(qū))在內(nèi)存中是放在一起的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域;
eg:int a;未初始化的。int a = 10;已初始化的。
常量區(qū)
- 常量區(qū)是編譯時分配的內(nèi)存空間,在程序結(jié)束后由系統(tǒng)釋放,主要存放已經(jīng)使用了的,且沒有指向的字符串常量
- 字符串常量因為可能在程序中被多次使用,所以`在程序運行之前就會提前分配內(nèi)存
代碼區(qū)(.text)
代碼區(qū)是編譯時分配主要用于存放程序運行時的代碼,代碼會被編譯成二進制存進內(nèi)存的
堆棧溢出
局部變量、函數(shù)的參數(shù)等都在桟里,而桟的大小肯定不是無限大的,桟由高到地,堆由地到高,當相互碰撞的時候,就會產(chǎn)升堆棧溢出。當使用遞歸的時候尤其要注意
-
擴展
一、虛擬內(nèi)存簡介
我們操作系統(tǒng)都是運行在內(nèi)存之上的,內(nèi)存一般都不大,所以為了支持多進程,也為了支持大程序,抽象的虛擬存儲的概念誕生了。
當我們向系統(tǒng)申請內(nèi)存時,系統(tǒng)并不會給你返回物理內(nèi)存的地址,而是給你一個虛擬內(nèi)存地址。每個進程都擁有相同大小的虛擬地址空間,對于32位的進程,可以擁有4GB的虛擬內(nèi)存,64位進程則更多,可達18EB。只有我們開始使用申請到的虛擬內(nèi)存時,系統(tǒng)才會將虛擬地址映射到物理地址上,從而讓程序使用真實的物理內(nèi)存。
1. RAM ROM
RAM:運行內(nèi)存,不能掉電存儲。ROM:存儲性內(nèi)存,可以掉電存儲,例如內(nèi)存卡、Flash。
RAM類型不具備掉電存儲能力(即一掉電數(shù)據(jù)消失),所以app程序一般存放于ROM中。
RAM的訪問速度要遠高于ROM,價格也要高。
2. App程序啟動
App程序啟動,系統(tǒng)會把開啟的那個App程序從Flash或ROM里面拷貝到內(nèi)存(RAM),然后從內(nèi)存里面執(zhí)行代碼。
另一個原因是CPU只能從RAM直接讀取指令(需要Flash驅(qū)動等等)。
3. 內(nèi)存分頁
系統(tǒng)會對虛擬內(nèi)存和物理內(nèi)存進行分頁,虛擬內(nèi)存到物理內(nèi)存的映射都是以頁為最小粒度的。在OSX和早期的iOS系統(tǒng)中,物理和虛擬內(nèi)存都按照4KB的大小進行分頁。iOS近期的系統(tǒng)中,基于A7和A8處理器的系統(tǒng),物理內(nèi)存按照4KB分頁,虛擬內(nèi)存按照16KB分頁?;贏9處理器的系統(tǒng),物理和虛擬內(nèi)存都是以16KB進行分頁。系統(tǒng)將內(nèi)存頁分為三種狀態(tài)。
活躍內(nèi)存頁(active pages)- 這種內(nèi)存頁已經(jīng)被映射到物理內(nèi)存中,而且近期被訪問過,處于活躍狀態(tài)。
非活躍內(nèi)存頁(inactive pages)- 這種內(nèi)存頁已經(jīng)被映射到物理內(nèi)存中,但是近期沒有被訪問過。
可用的內(nèi)存頁(free pages)- 沒有關(guān)聯(lián)到虛擬內(nèi)存頁的物理內(nèi)存頁集合。
當可用的內(nèi)存頁降低到一定的閥值時,系統(tǒng)就會采取低內(nèi)存應(yīng)對措施,在OSX中,系統(tǒng)會將非活躍內(nèi)存頁交換到硬盤上,而在iOS中,則會觸發(fā)Memory Warning,如果你的App沒有處理低內(nèi)存警告并且還在后臺占用太多內(nèi)存,則有可能被殺掉。
二、對象內(nèi)存占比
1、幾種數(shù)據(jù)類型

2、對象NSObject
2.1 NSObject對象占用的內(nèi)存空間
- 任意一個NSObject對象都會分配16byte的內(nèi)存空間,通過源碼可以知道:
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
通過代碼可以知道,蘋果內(nèi)存分配最小內(nèi)存字節(jié)數(shù)為16 bytes.
2.2 OC對象實際使用內(nèi)存空間
根據(jù)設(shè)備的cpu是64位還是32位的,
- 64位的占用了 8 Bytes
- 32位的使用了 4 bytes
在64位情況下,OC實例對象成員變量所占的大小,實際上是 8 字節(jié),下面可以通過源碼來驗證一下:
#import <Objc/Runtime>
Class_getInstanceSize([NSObject Class])
size_t class_getInstanceSize(Class cls) {
if (!cls) return 0;
return cls->alignedInstanceSize();
}
Obj-C指針所指向的內(nèi)存的大小,實際上是16 字節(jié)
#import <malloc/malloc.h>
malloc_size((__bridge const void *)obj);
對象在分配內(nèi)存空間時,會進行內(nèi)存對齊,所以在iOS 中,分配內(nèi)存空間都是16字節(jié)的倍數(shù)。16字節(jié)是蘋果設(shè)定的內(nèi)存的最小單位。
tips 內(nèi)存對齊規(guī)則
請牢記以下3條原則:(在沒有#pragma pack宏的情況下,務(wù)必看完最后一行)
原則 1: 數(shù)據(jù)成員對齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大?。ㄖ灰摮蓡T有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int在32位機為4字節(jié),則要從4的整數(shù)倍地址開始存儲。
原則 2: 結(jié)構(gòu)體作為成員:如果一個結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲.(struct a里存有struct b,b里有char,int ,double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲.)
原則 3: 收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,.必須是其內(nèi)部最大成員的整數(shù)倍.不足的要補齊.
內(nèi)存對齊規(guī)則 實踐
typedef struct bb
{
int id; //[0]....[3]
double weight; //[8].....[15] 原則1
float height; //[16]..[19],總長要為8的整數(shù)倍,補齊[20]...[23] 原則3
}BB;
typedef struct aa
{
char name[2]; //[0],[1]
int id; //[4]...[7] 原則1
double score; //[8]....[15]
short grade; //[16],[17]
BB b; //[24]......[47] 原則2
}AA;
int main()
{
AA a;
cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
return 0;
}
結(jié)果是
48 24
ok,上面的全看明白了,內(nèi)存對齊基本過關(guān).
再講講#pragma pack().
在代碼前加一句#pragma pack(1),你會很高興的發(fā)現(xiàn),上面的代碼輸出為
32 16
bb是4+8+4=16,aa是2+4+8+2+16=32;
- 這不是理想中的沒有內(nèi)存對齊的世界嗎.沒錯,
#pragma pack(1),告訴編譯器,所有的對齊都按照1的整數(shù)倍對齊,換句話說就是沒有對齊規(guī)則.
明白了不?
那#pragma pack(2)的結(jié)果又是多少呢?對不起,5分鐘到了,自己去測試吧.
ps:Vc,Vs等編譯器默認是#pragma pack(8),所以測試我們的規(guī)則會正常;注意gcc默認是#pragma pack(4),并且gcc只支持1,2,4對齊。套用三原則里計算的對齊值是不能大于#pragma pack指定的n值。
三、內(nèi)存管理
MRR,手動管理----manual retain-release
ARC,自動引用計數(shù)---automatic reference counting
MRC,手動引用計數(shù)----Manual Reference Counting
Objective-C提供了三種內(nèi)存管理方式:manual retain-release(MRR,手動管理),automatic reference counting(ARC,自動引用計數(shù)),garbage collection(垃圾回收)。iOS不支持垃圾回收;ARC作為蘋果新提供的技術(shù),蘋果推薦開發(fā)者使用ARC技術(shù)來管理內(nèi)存
MRR和MRC兩者都是指手動管理內(nèi)存。。。兩者其實并沒有什么區(qū)別。MRC雖然大家都說,相對于ARC所以才有了MRC這個詞。但個人覺得不如MRR描述更貼切。個人習(xí)慣問題吧。
iOS引用計數(shù)原理
- 引用計數(shù)機制只使用在堆中,那么所有不保存在堆中的數(shù)據(jù)的引用計數(shù)都為-1。
- 在OC中幾乎所有不可變對象(常量)都存在常量區(qū),內(nèi)存管理由系統(tǒng)來做,引用計數(shù)為-1。
- 對象引用計數(shù)降至0,那么對象所在的內(nèi)存也許會回收。
- retain 遞增引用計數(shù)
- release 遞減引用計數(shù)
- autorelease 清理「自動釋放池」時,在遞減保留計數(shù)
> [參考](http://www.itdecent.cn/p/67970ff59ffc)
出處:
鏈接:http://www.itdecent.cn/p/3047ade482c7
鏈接:http://www.itdecent.cn/p/86c092571024
鏈接:https://blog.csdn.net/ferrarifomaul/article/details/82392867
鏈接:http://www.itdecent.cn/p/252526847be5
鏈接:https://sg.jianshu.io/p/b88003b20eea
鏈接:https://blog.csdn.net/hairetz/article/details/4084088
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。