iOS內(nèi)存管理(三)SideTables詳解

SideTables分析

SideTables與iOS內(nèi)存管理息息相關(guān)圃阳,今天就來研究一下SideTables怀读,先看一下SideTables的定義

static StripedMap<SideTable>& SideTables() {
    return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}

SideTablesde的實(shí)質(zhì)類型是存儲SideTable的StripedMap蘸炸。在StripedMap類中有StripeCount定義存儲sidetable的最大數(shù)量揽涮,所以每個(gè)SideTablesdes可以對應(yīng)多個(gè)對象,而每個(gè)對象對應(yīng)一個(gè)sidetable。

SideTable中包含三個(gè)成員桦卒,自旋鎖,引用計(jì)數(shù)表匿又,弱引用表方灾。

- spinlock_t slock;
- RefcountMap refcnts;
- weak_table_t weak_table;

這里的slock是一個(gè)自旋鎖,就是為了保證多線程訪問安全性

refcnts本質(zhì)是一個(gè)存儲對象引用計(jì)數(shù)的hash表碌更,key為對象裕偿,value為引用計(jì)數(shù)(優(yōu)化過得isa中,引用計(jì)數(shù)主要存儲在isa中)

weak_table是存儲對象弱引用的一個(gè)結(jié)構(gòu)體痛单,該結(jié)構(gòu)體內(nèi)成員如下

- weak_entry_t *weak_entries;
- size_t    num_entries;
- uintptr_t mask;
- uintptr_t max_hash_displacement;

簡單介紹完SideTables相關(guān)數(shù)據(jù)結(jié)構(gòu)關(guān)系嘿棘,下面來逐個(gè)分析分析一下

StripedMap

StripedMap是定義在objc-private.h中的一個(gè)類,具體代碼太長旭绒,不再這里貼了鸟妙,只介紹里面重要的內(nèi)容

1、StripeCount定義了里面存儲對象最大數(shù)量挥吵。

enum { StripeCount = 8 };

2重父、定義結(jié)構(gòu)體PaddedT包裝傳入泛型(這里指的是SideTable),這里使alignas(CacheLineSize)方法使字節(jié)對齊忽匈。猜測字節(jié)對齊的目的是提高存取hash值時(shí)的效率房午。

struct PaddedT {
    T value alignas(CacheLineSize);
};

3、實(shí)現(xiàn)了index計(jì)算的hash算法indexForPointer

static unsigned int indexForPointer(const void *p) {
    uintptr_t addr = reinterpret_cast<uintptr_t>(p);
    return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}

4脉幢、獲取sidetable的操作getLock方法

const void *getLock(int i) {
    if (i < StripeCount) return &array[i].value;
    else return nil;
}

5歪沃、其他鎖操作array[i].value.lock,或者array[i].value.unlock(),調(diào)用的是sidetable中的鎖

void lockAll() {
    for (unsigned int i = 0; i < StripeCount; i++) {
        array[i].value.lock();
    }
}

6嫌松、構(gòu)造方法StripedMap(),具體實(shí)現(xiàn)沒有開元沪曙,只能看出DEBUG模式下有部分操作

#if DEBUG
StripedMap() {
    // Verify alignment expectations.
    uintptr_t base = (uintptr_t)&array[0].value;
    uintptr_t delta = (uintptr_t)&array[1].value - base;
    assert(delta % CacheLineSize == 0);
    assert(base % CacheLineSize == 0);
}
#else
constexpr StripedMap() {}
#endif

引用計(jì)數(shù)refcnts 存儲結(jié)構(gòu)RefcountMap

RefcountMap定義如下,他的類型是objc::DenseMap萎羔。

typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;

三個(gè)參數(shù)分別代表對象的hash key液走,引用計(jì)數(shù),是否需要在引用計(jì)數(shù)為0的時(shí)候自動釋放相應(yīng)的hash節(jié)點(diǎn)贾陷。這里默認(rèn)傳true缘眶。所以對象的引用計(jì)數(shù)refcnts不一定存在。在優(yōu)化過得isa中由extra_rc來存儲引用計(jì)數(shù)髓废,只有其存儲計(jì)數(shù)上溢出的時(shí)候才會存入sidetable中的refcnts巷懈。

弱引用表 weak_table_t weak_table

上面有說過一個(gè)sidetable中存儲多個(gè)對象的信息,弱引用表weak_table_t又是一個(gè)結(jié)構(gòu)體慌洪,所以在weak_table_t里面也存儲著多個(gè)對象信息顶燕。其中weak_entries是一個(gè)hash數(shù)組凑保,存儲弱引用對象的相關(guān)信息weak_entry_tnum_entries表示weak_entries數(shù)組中元素個(gè)數(shù)涌攻。另外兩個(gè)參數(shù)maskmax_hash_displacement 都是uintptr_t(無符號long)類型的欧引,mask一般是做位運(yùn)算定義的值。max_hash_displacement則表示hash沖突的最大次數(shù)

下面的代碼是weak_table中通過對象獲取weak_entry_t的方法恳谎。在這個(gè)方法中可以看出mask餐椅了hash值的計(jì)算芝此,hash沖突次數(shù)超過max_hash_displacement之后,就直接返回nil因痛。

使用hash_pointer(referent) 和 weak_table->mask進(jìn)行與運(yùn)算婚苹。通過&運(yùn)算保證index不會越界,所以可以推測出mask的值為hash數(shù)組長度-1鸵膏。

這里hash沖突的解決方案是計(jì)算出hash位置index租副,判斷index中存儲referent是否與目標(biāo)referent相等,不相等的話后移一位繼續(xù)判斷较性,并將hash_displacement++,記錄移動次數(shù)结胀。當(dāng)hash_displacement的值大于max_hash_displacement時(shí)赞咙,直接返回nil。當(dāng)index == begin時(shí)糟港,即遍歷一圈也沒找到目標(biāo)對象攀操,直接調(diào)用bad_weak_table報(bào)錯。

static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    assert(referent);

    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;

    size_t begin = hash_pointer(referent) & weak_table->mask;
    size_t index = begin;
    size_t hash_displacement = 0;
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        if (index == begin) bad_weak_table(weak_table->weak_entries);
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    return &weak_table->weak_entries[index];
}

weak_entry_t

weak_entry_t存儲著某個(gè)對象的弱引用信息秸抚,又是一個(gè)結(jié)構(gòu)體速和。跟weak_table_t類似,里面存儲一個(gè)hash表weak_referrer_t剥汤,弱引用該"對象的指針"的指針颠放。通過weak_referrer_t的操作,可以使該對象的弱引用指針在對象釋放后吭敢,置為nil碰凶。

weak_entry_t的定義如下:

struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            weak_referrer_t *referrers;
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };

    bool out_of_line() {
        return (out_of_line_ness == REFERRERS_OUT_OF_LINE);
    }

    weak_entry_t& operator=(const weak_entry_t& other) {
        memcpy(this, &other, sizeof(other));
        return *this;
    }

    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};

DisguisedPtr<objc_object> referent :弱引用對象指針摘要。其實(shí)可以理解為弱引用對象的指針鹿驼,只不過這里使用了摘要的形式存儲欲低。(所謂摘要,其實(shí)是把地址取負(fù))畜晰。

weak_referrer_t:這是一個(gè)共用體砾莱,分動態(tài)數(shù)組和固定長度數(shù)組兩種情況,

out_of_line:bool類型區(qū)分是weak_referrer_t中數(shù)組類型

weak_entry_t& operator=(const weak_entry_t& other):賦值

weak_entry_t(objc_object *newReferent, objc_object **newReferrer) 構(gòu)造

指針數(shù)組weak_referrer_t

typedef DisguisedPtr<objc_object *> weak_referrer_t;

weak_referrer_t以指針摘要的形式凄鼻,存儲 弱引用指針 的指針腊瑟。weak_referrer_t數(shù)組這里設(shè)置了兩種模式聚假,畢竟動態(tài)數(shù)組涉及到更多的操作,在小數(shù)據(jù)量的情況下扫步,使用定長數(shù)組效率更高魔策。

總結(jié)

全局維護(hù)一個(gè)sidetables,

sidetables里面包含多個(gè)sidetabl河胎,可以通過對象的hash查找到對象存在的sidetable闯袒。

一個(gè)sidetable對應(yīng)多個(gè)對象。里面有一個(gè)引用計(jì)數(shù)表游岳,一個(gè)弱引用表

再次對對象hash計(jì)算值可以從sidetable中RefcountMap中獲取對象引用計(jì)數(shù)

從weak_table_t中保存著的一個(gè)sidetable中所有weak_entries表

從weak_entries中通過對象查找著某個(gè)對象對應(yīng)的弱引用信息weak_entry_t

weak_entry_t中保存著弱引用該對象的 指針地址的hash數(shù)組

弱引用表使用舉例

其他還好理解政敢,弱引用比較繞。這里舉個(gè)實(shí)例

NSObject *obj = [[NSObject alloc] init];
__weak id  weakObj = obj;

在這段代碼里胚迫,用weakObj指向obj時(shí)

1喷户、會通過obj從sidetables中找到sidetable
2、找到sidetable中的弱引用表weak_table_t
3访锻、通過obj從weak_table_t中的weak_entries找到obj對應(yīng)的weak_entry_t
4褪尝、在obj對應(yīng)的weak_entry_t的weak_referrer_t中加入weakObj指針

當(dāng)obj釋放時(shí),會判斷obj的weakly_referenced是否為1期犬,即obj是否被弱引用河哑。如果被弱引用,則進(jìn)行下面的操作

1龟虎、會通過obj從sidetables中找到sidetable
2璃谨、找到sidetable中的弱引用表weak_table_t
3、通過obj從weak_table_t中的weak_entries找到obj對應(yīng)的weak_entry_t
4鲤妥、查找weak_entry_t中的weak_referrer_t數(shù)組佳吞,并將weak_referrer_t中存儲的指針(這里指weakObj)指向nil
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市棉安,隨后出現(xiàn)的幾起案子底扳,更是在濱河造成了極大的恐慌,老刑警劉巖垂券,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件花盐,死亡現(xiàn)場離奇詭異,居然都是意外死亡菇爪,警方通過查閱死者的電腦和手機(jī)算芯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凳宙,“玉大人熙揍,你說我怎么就攤上這事∈仙” “怎么了届囚?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵有梆,是天一觀的道長。 經(jīng)常有香客問我意系,道長泥耀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任蛔添,我火速辦了婚禮痰催,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘迎瞧。我一直安慰自己夸溶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布凶硅。 她就那樣靜靜地躺著缝裁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪足绅。 梳的紋絲不亂的頭發(fā)上捷绑,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音氢妈,去河邊找鬼胎食。 笑死,一個(gè)胖子當(dāng)著我的面吹牛允懂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衩匣,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼蕾总,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琅捏?” 一聲冷哼從身側(cè)響起生百,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柄延,沒想到半個(gè)月后蚀浆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搜吧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年市俊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤奈。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡摆昧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蜒程,到底是詐尸還是另有隱情绅你,我是刑警寧澤伺帘,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站忌锯,受9級特大地震影響伪嫁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜偶垮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一张咳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧针史,春花似錦晶伦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至频祝,卻和暖如春泌参,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背常空。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工沽一, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人漓糙。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓铣缠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昆禽。 傳聞我的和親對象是個(gè)殘疾皇子蝗蛙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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