內(nèi)存管理-(一)內(nèi)存管理基礎知識

先來了解一下iOS中的內(nèi)存布局兰粉。

Xnip2018-10-24_22-54-39.png

上面的圖代表的是內(nèi)存區(qū)域扮惦,最上方是內(nèi)核區(qū),最下面是保留的內(nèi)存空間亲桦。中間位置是給程序加載使用的空間。程序被加載到內(nèi)存浊仆,會分為三部分客峭。

  • 未初始化數(shù)據(jù)(.bss),未初始化的靜態(tài)變量抡柿,全局變量等
  • 已初始化數(shù)據(jù)(.data)舔琅,已初始化的靜態(tài)變量,全局變量等
  • 代碼段(.text)洲劣,程序代碼就存在這個區(qū)域

iOS中定義的一些方法函數(shù)都是在棧上進行工作的备蚓,棧是從高地址到低地址擴展。而對象囱稽,或者是被copy的block都存在于堆中郊尝,推是由低地址向高地址擴展的。

棧(satck): 方法調(diào)用主要在這個內(nèi)存區(qū)域中進行

堆(heap): 通過alloc等分配的對象存在于這個區(qū)域

未初始化數(shù)據(jù)(.bss): 未初始化的靜態(tài)變量战惊,全局變量等

已初始化數(shù)據(jù)(.data): 已初始化的靜態(tài)變量流昏,全局變量等

代碼段(.text): 程序代碼存放區(qū)域

內(nèi)存管理方案

在iOS中,內(nèi)存管理方案分為以下幾種:

  • TaggedPointer(小對象吞获,如NSNumber等)
  • NONPOINTER_ISA(64位架構(gòu)下的內(nèi)存管理方案况凉,通常內(nèi)存中表示地址只需要32-40位即可,多出的區(qū)域用來存儲其他的數(shù)據(jù)各拷,以節(jié)省內(nèi)存)
  • 散列表
    • 引用計數(shù)表
    • 弱引用表
    • ...

NONPOINTER_ISA

在arm64架構(gòu)下刁绒,我們來看看NONPOINTER_ISA所表示的是怎樣的結(jié)構(gòu)

Xnip2018-10-25_13-17-37.png
Xnip2018-10-25_13-26-10.png
  1. 第1位。是一個叫indexed的標志位烤黍,如果為0知市,表示這個isa指針是一個純的地址指針(保存的都是類對象的地址)傻盟。1則表示這個isa指針除去類對象地址外還保存著一些關于內(nèi)存管理的其他的數(shù)據(jù)。
  2. 第2位初狰。has_assoc,表示當前對象是否有關聯(lián)對象莫杈,0代表沒有。
  3. 第3位奢入。has_cxx_dtor筝闹,代表當前對象是否有使用到C++相關的內(nèi)容。0表示沒有腥光。
  4. 第4-35位关顷。shiftcls,這33位都保存著isa所指向的類對象的指針地址武福。
  5. 第36-41位议双。magic,
  6. 第42位捉片。weakly_referenced平痰,用來標識對象是否有相應的弱引用指針。
  7. 第43位伍纫。deallocating宗雇,用來標識對象是否正在被銷毀。
  8. 第44位莹规。has_sidetable_rc赔蒲,表示當前isa指針當中存儲的引用計數(shù)已經(jīng)達到了上限,則需要外掛一個sidetable(散列表)的數(shù)據(jù)結(jié)構(gòu)來存儲相關的引用計數(shù)內(nèi)容良漱。
  9. 第45-63位舞虱。extra_rc,存儲的就是當前isa指針的引用計數(shù)(在引用計數(shù)不夠大時母市,會使用這塊區(qū)域矾兜,太大就外掛散列表)。

散列表

下面就來看看SideTables的結(jié)構(gòu)

// 這是一個Hash表
static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

我們可以看到患久,這下面掛了很多SideTable結(jié)構(gòu)體焕刮。

再來看看SideTable里面是什么

struct SideTable {
    spinlock_t slock;   // 自旋鎖
    RefcountMap refcnts;    // 引用計數(shù)表
    weak_table_t weak_table;    // 弱引用表

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    // Address-ordered lock discipline for a pair of side tables.

    template<HaveOld, HaveNew>
    static void lockTwo(SideTable *lock1, SideTable *lock2);
    template<HaveOld, HaveNew>
    static void unlockTwo(SideTable *lock1, SideTable *lock2);
};

先來思考一個問題,為什么要使用SideTables墙杯,而不是一個SideTable來表示呢配并?

如果說,將所有的引用計數(shù)高镐,都放在一張大表中溉旋,那么我們對某個對象的引用計數(shù)進行操作時,會進行加鎖的操作嫉髓。同一時刻只有一個線程可以訪問到這個表观腊,這樣其他的線程就會造成阻塞邑闲。等待持有這個鎖的線程釋放才可以進行引用計數(shù)表的操作。但是梧油,如果說有多張表苫耸,就可以一定程度上解決這個問題。而這種解決方案叫做分離鎖儡陨。也就是將一個大的共享資源褪子,拆分成多個,并分別加鎖骗村。

那么嫌褪,問題又來了,如何實現(xiàn)快速分流(如何快速的通過對象指針找到對象到底是屬于哪個SideTable)胚股?

首先笼痛,SideTables的本質(zhì)是一張Hash表。對象指針通過Hash函數(shù)的計算琅拌,會計算出一個值缨伊。來決定對象鎖對應的SideTable是哪張。

SideTable

現(xiàn)在我們來剖析一下SideTable的數(shù)據(jù)結(jié)構(gòu):

  • spinlock_t
  • RefcountMap
  • weak_table_t
spinlock_t(自旋鎖):
  • spinlock_t是忙等(如果鎖被其他線程獲取进宝,當前線程會不斷的探測鎖是否被釋放)的鎖
  • 適用于輕量訪問(簡單的計算)
RefcountMap(引用計數(shù)表):

這也是一個Hash表倘核,通過指針可以找到對象的引用計數(shù)

Xnip2018-10-25_14-34-24.png

這里說明一下為什么用Hash表,Hash表結(jié)構(gòu)的本質(zhì)即彪,是將數(shù)據(jù)插入數(shù)組時,使用函數(shù)計算其位置活尊,取出時也通過這個函數(shù)隶校,取得位置,避免了大量的遍歷操作蛹锰,從而提升效率深胳。

size_t

是一個unsign long型的變量。我們再來看看其內(nèi)存的存儲結(jié)構(gòu):

Xnip2018-10-25_14-38-30.png

第一位代表是否有弱引用铜犬。第二位表示當前對象是否在進行銷毀舞终。后面的才是引用技術計數(shù)值。因此癣猾,我們在獲取對象的引用計數(shù)值的時候敛劝,需要向右偏移兩位,才可以取得真實的引用計數(shù)值纷宇。

weak_table_t(弱引用表)

這也是一個Hash表夸盟,通過指針可以找到對象在表中的存儲位置。

Xnip2018-10-25_14-43-25.png

weak_entry_t是一個結(jié)構(gòu)體數(shù)組像捶。其中存儲的就是實際的弱引用指針上陕。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桩砰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子释簿,更是在濱河造成了極大的恐慌亚隅,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庶溶,死亡現(xiàn)場離奇詭異煮纵,居然都是意外死亡,警方通過查閱死者的電腦和手機渐尿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門醉途,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人砖茸,你說我怎么就攤上這事隘擎。” “怎么了凉夯?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵货葬,是天一觀的道長。 經(jīng)常有香客問我劲够,道長震桶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任征绎,我火速辦了婚禮蹲姐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘人柿。我一直安慰自己柴墩,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布凫岖。 她就那樣靜靜地躺著江咳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪哥放。 梳的紋絲不亂的頭發(fā)上歼指,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音甥雕,去河邊找鬼踩身。 笑死,一個胖子當著我的面吹牛社露,可吹牛的內(nèi)容都是我干的惰赋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赁濒!你這毒婦竟也來了轨奄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤拒炎,失蹤者是張志新(化名)和其女友劉穎挪拟,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體击你,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡玉组,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了丁侄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惯雳。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸿摇,靈堂內(nèi)的尸體忽然破棺而出石景,到底是詐尸還是另有隱情,我是刑警寧澤拙吉,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布潮孽,位于F島的核電站,受9級特大地震影響筷黔,放射性物質(zhì)發(fā)生泄漏往史。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一佛舱、第九天 我趴在偏房一處隱蔽的房頂上張望椎例。 院中可真熱鬧,春花似錦请祖、人聲如沸订歪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至撒犀,卻和暖如春福压,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背或舞。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工荆姆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人映凳。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓胆筒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子仆救,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,090評論 1 32
  • 1抒和、內(nèi)存布局 stack:方法調(diào)用 heap:通過alloc等分配對象 bss:未初始化的全局變量等。 data:...
    AKyS佐毅閱讀 1,592評論 0 19
  • 通過以下方法查看iOS的引用計數(shù)管理: alloc retain release retainCount deal...
    fou7閱讀 699評論 0 4
  • 一日小雨彤蔽,山中自在摧莽。聽張先生說,這種苔類存活對環(huán)境的要求特別高顿痪,在昌溪镊辕,我見到好多。下雨的時候蚁袭,就愈發(fā)蔥綠得可愛了...
    TanninLyu閱讀 278評論 0 0
  • 今天是4月1號征懈,愚人節(jié)。昨晚揩悄,聽了一夜張國榮卖哎。是非有公理,不再像以往那般笨虏束,笑罵由人棉饶,輕快笑著行… 生活,是一次次...
    黑白回憶閱讀 113評論 0 0