iOS內(nèi)存五大區(qū)

內(nèi)存分區(qū).png
  • 如圖:內(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)提供一些必要空間
內(nèi)存5大區(qū).png
桟 (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ù)類型
類型占比內(nèi)存.png
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)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言: 在iOS開發(fā)中,平常大家都會說,堆區(qū),棧區(qū),都是存在虛擬內(nèi)存。 虛擬內(nèi)存五大區(qū):堆區(qū)、棧區(qū)、全局區(qū)、常量區(qū)...
    淺墨入畫閱讀 690評論 0 2
  • 內(nèi)存五大區(qū) 在iOS開發(fā)中,平常大家都會說,堆區(qū),棧區(qū),都是存在虛擬內(nèi)存。今天來淺談一波存在虛擬內(nèi)存的五大區(qū) 內(nèi)存...
    搬運工iOS橙閱讀 489評論 0 0
  • 在iOS中,內(nèi)存主要分為棧區(qū)、堆區(qū)、全局區(qū)、常量區(qū)、代碼區(qū)五大區(qū)域。如下圖所示 下面分別介紹這五大區(qū) 棧區(qū)(Sta...
    輝輝歲月閱讀 1,038評論 0 1
  • 內(nèi)存主要分為棧區(qū)、堆區(qū)、全局區(qū)、常量區(qū)、代碼區(qū)五大區(qū)域。如下圖所示 棧區(qū)(Stack) 定義棧是系統(tǒng)數(shù)據(jù)結(jié)構(gòu),其對...
    學(xué)不來的凡人閱讀 1,155評論 0 4
  • iOS內(nèi)存主要分成棧區(qū)、堆區(qū)、全局區(qū)、常量區(qū)、代碼區(qū)這五大區(qū)域。如下圖所示: 棧(Stack) 棧是由高地址向低地...
    iOSer_jia閱讀 1,015評論 0 2

友情鏈接更多精彩內(nèi)容