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