内存管理相关【内存布局内存管理方案】
iOS系統(tǒng)下的內(nèi)存布局
最上面是內(nèi)核區(qū),最下面是保留區(qū),中間是給程序加載的空間。
從高地址到低地址依次為內(nèi)核區(qū)、棧、堆、靜態(tài)全局區(qū)(未初始化區(qū)域.bss和已初始化區(qū)域.data)、代碼區(qū)、保留區(qū);
程序被加載到內(nèi)存分成三段未初始化數(shù)據(jù)(.bss)、已初始化數(shù)據(jù)(.data)和代碼段(.text)。
代碼段顧名思義存放代碼;
已初始化區(qū)域:已經(jīng)初始化聲明的靜態(tài)變量和全局變量;
未初始化區(qū)域:未初始化的靜態(tài)變量和全局變量;
堆heap:創(chuàng)建的對象或被copy的block;
棧stack:定義的方法或者函數(shù)都存放在棧上,由高地址向低地址,向下擴(kuò)展
內(nèi)存管理方案
ios管理系統(tǒng)針對不同的場景提供不同的內(nèi)存管理方案。
·TaggedPointer:對一些小對象使用,如NSNumber
·NONPOINTER_ISA:非指針型的ISA;應(yīng)用于64位架構(gòu)下的iOS應(yīng)用程序。在64位架構(gòu)下,isa指針占64個比特位,實(shí)際上有32位或者40位就夠用了,剩余的比特位就浪費(fèi)了。為了不讓內(nèi)存浪費(fèi)更好的管理內(nèi)存,剩余的32位蘋果用來存儲和內(nèi)存管理相關(guān)的內(nèi)容。
·散列表:是一個復(fù)雜的數(shù)據(jù)結(jié)構(gòu),其中包含了引用計數(shù)表和弱引用表。
【詳解NONPOINTER_ISA】
在64位架構(gòu)下:
第0位叫indexed的標(biāo)志位,如果是0,代表它是一個isa指針,表示當(dāng)前對象的類對象的地址;如果是1,代表它不僅是一個isa指針,當(dāng)前對象的類對象的地址,還存儲內(nèi)存管理相關(guān)的內(nèi)容。
第一位has_assoc表示當(dāng)前對象是否有關(guān)聯(lián)對象,0表示沒有,1表示有。
第二位has_cxx_dtor代表當(dāng)前對象是否有c++代碼
第三位 3···31、32···35到第35位共33位,表示當(dāng)前對象的類對象的內(nèi)存地址。
第36···41位共6位,是magic 字段。
第42位weakly_referenced表示是否含有弱引用指針。
第43位deallocating表示當(dāng)前指針是否正在進(jìn)行dealloc操作。
第44位has_sidetable_rc表示當(dāng)前isa指針的引用計數(shù)是否達(dá)到上限,如果達(dá)到上限需要外掛一個sidetable,來額外存儲相關(guān)的引用計數(shù)內(nèi)容。
第45···63位extra_rc表示額外的引用計數(shù),儲存內(nèi)存管理相關(guān)的,當(dāng)引用計數(shù)很小的時候,就直接存在isa指針當(dāng)中。
如下圖: ?
【詳解sidetable】
散列表是通過SideTables()結(jié)構(gòu)來實(shí)現(xiàn)的,sidetables下面掛了很多sidetable,在不同的架構(gòu)下是有不同個數(shù)的,比如說在非嵌入式系統(tǒng)中sidetable有64個。
sideTables()實(shí)際上是一個哈希表(hash),可以通過它的對象指針,找到它對應(yīng)的引用計數(shù)表或者弱引用表具體在哪個sidetable中。
sidetable有自旋鎖(spinlock_t),引用計數(shù)表(refcountMap)和弱引用表(weak_table_t)。
sideTable結(jié)構(gòu):
自旋鎖:是忙等的鎖。如果鎖已經(jīng)被其他線程獲取,那么當(dāng)前線程會自己去不斷的獲取是否被釋放,直到其他線程釋放。適用于輕量訪問。如+1,-1。
引用計數(shù)表:是hash表,其實(shí)就是hash查找,插入和查找通過同一個hash函數(shù),避免了循環(huán)遍歷,提高了查找效率。
size_t實(shí)際上是一個無符號long型(unsign long)的變量。在獲取對象的真實(shí)的引用計數(shù)值時,需要向右偏移兩位。
弱引用表:weak_table_t也是一個hash表。
思考如下問題:
為什么不是一個sidetable?而是有多個sidetable組成一個sideTables;或者說sidetables為什么是多張表,而不是一張表?
解析:
假設(shè)只有一張sidetable表,那么內(nèi)存中的所有對象的引用計數(shù)或者弱引用都存儲在這張表中,這個時候,如果我們要對某一個對象的引用計數(shù)值進(jìn)行操作,+1或者-1;由于不同的對象在不同的線程中,不同的線程操作同一張表,就有資源訪問的問題,那么我們要對這張大表進(jìn)行加鎖操作來保證數(shù)據(jù)訪問的安全性。在這個過程中就會產(chǎn)生一個效率問題,比如,成千上萬的對象進(jìn)行引用計數(shù)操作,那么需要加鎖排隊,就會有效率問題。比如,現(xiàn)在已經(jīng)有一個對象在操作這個大表,那么下個對象就要等前對象操作結(jié)束,把鎖釋放之后,它才能操作這張表。系統(tǒng)為了解決這種效率問題,引入了分離鎖。分離鎖就是把一個大表分成幾個小表,A、B分別在不同表中,同時進(jìn)行操作的話,可以并發(fā)進(jìn)行,這樣就提高了訪問效率。
思考如下問題:
怎樣實(shí)現(xiàn)快速分流?找到當(dāng)前對象在哪張表中?【快速分流指給出一個對象的指針,如何快速的定位到這個這個對象在哪張表中】
解析:
sidetables本質(zhì)是一張Hash表。
什么是Hash表?有一個指針對象key,通過hash函數(shù)的運(yùn)算,得到一個值value,找到對應(yīng)的sidetable。
思考如下問題:
你是否使用過自旋鎖,自旋鎖和普通鎖有什么區(qū)別?適用于哪些場景?
解析:
自旋鎖是忙等的鎖,適用于輕量訪問。
思考如下問題:
引用計數(shù)表示通過什么實(shí)現(xiàn)的?為什么引用計數(shù)表要用hash表來實(shí)現(xiàn)呢?
解析:
是通過hash表實(shí)現(xiàn)的。插入和查找都通過同一個hash函數(shù),避免了循環(huán)遍歷,提高了查找效率。
關(guān)于內(nèi)存管理的討論全部基于objc-runtime-680版本講解。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的内存管理相关【内存布局内存管理方案】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Block相关内容梳理
- 下一篇: 小白的AFNetWorking之路