內(nèi)存對(duì)齊探索

本篇探索依舊是基于objc以及libmalloc源碼晦攒,源碼下載及配置請(qǐng)參考本篇文章态贤。

一气笙、對(duì)齊原因:

1次企、平臺(tái)原因(移植原因):不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的行冰;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù)婚肆,否則拋出硬件異常。

2柿冲、性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊谭期。原因在于堵第,為了訪問未對(duì)齊的內(nèi)存吧凉,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問踏志。

(來源百度百科)

二阀捅、對(duì)齊規(guī)則

1、數(shù)據(jù)成員對(duì)齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員针余,第一個(gè)數(shù)據(jù)成員放在offset為0的地方饲鄙,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要改成員有子成員,比如數(shù)組圆雁,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié)忍级,則要從4的整數(shù)倍地址開始存儲(chǔ))。

2伪朽、結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員轴咱,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開始存儲(chǔ)(struct A里有struct BB里有char,int,double等元素烈涮,那B應(yīng)該從8的整數(shù)倍開始存儲(chǔ))朴肺。

3、收尾工作:就夠提的總大小跃脊,也就是sizeof的結(jié)果宇挫,必須是其內(nèi)部最大成員的整數(shù)倍,不足的要補(bǔ)齊酪术。

4器瘪、32位系統(tǒng)下4字節(jié)對(duì)齊,64位系統(tǒng)下8字節(jié)對(duì)齊绘雁。(本篇例子均默認(rèn)8字節(jié)對(duì)齊)

三橡疼、結(jié)合代碼分析

1、結(jié)構(gòu)體中內(nèi)存對(duì)齊分析

struct struct1 {
    int a; // 4 字節(jié)
    char b; // 1 字節(jié)
    double c; // 8 字節(jié)
    int *d; // 8 字節(jié)   
}MyStruct1;

struct struct2 {
    int a; // 4 字節(jié) 
    int *b; // 8 字節(jié) 
    char c; // 1 字節(jié) 
    struct struct1 myStruct; // 8字節(jié) 
    double d; // 8 字節(jié)
} MyStruct2;

1) 內(nèi)存分析:
括號(hào)內(nèi)為補(bǔ)齊位

  • MyStruct1:
    int a; 0-3 共4字節(jié)
    char b; 4 共5字節(jié)
    double c; (5,6,7) 8-15 共16字節(jié)
    int *d; 16-23 共24字節(jié)
    8字節(jié)對(duì)齊后為24

  • MyStruct2:
    int a; 0-3 共4字節(jié)
    int *b (4,5,6,7) 8-15字節(jié) 共16字節(jié)
    char c 16 字節(jié) 共17字節(jié)
    struct struct2 myStruct: (17,18,19,20,21,22,23) 24+24 = 47 共48字節(jié)
    double d: 48-55 共56字節(jié)
    8字節(jié)對(duì)齊后為56

2庐舟、對(duì)象及屬性中內(nèi)存對(duì)齊分析

Teacher  *p = [Teacher alloc];
p.name = @"TRACER";   //  8
p.age  = 18;            //  4
p.height = 185;         //  8
p.hobby  = @"女";       //  8
// p.sex    = 2;
p.ch1    = 'a';     //  1
p.ch2    = 'b';    //  1

1) 打印屬性以及對(duì)象分別所占的內(nèi)存空間欣除。

NSLog(@"%lu",class_getInstanceSize([p class])); // 40
NSLog(@"%lu",malloc_size((__bridge const void *)(p))); // 48

2) 屬性內(nèi)存優(yōu)化:

① 源碼:

// 64位下 WORD_MASK為7,也就是8字節(jié)對(duì)齊
static inline uint32_t word_align(uint32_t x) {
    // (x + 7) >> 3 << 3
    return (x + WORD_MASK) & ~WORD_MASK;
}

LLDB分析:

(lldb) x/6xg p
0x101044580: 0x001d80010000256d 0x0000001200006261
0x101044590: 0x00000001000020a0 0x00000000000000b9
0x1010445a0: 0x00000001000020c0 0x0000000000000000
(lldb) po 0x0000001200006261
10372493740046155960

(lldb) po 0x00000001000020a0
TRACER

(lldb) po 0x00000000000000b9
185

(lldb) po 0x00000001000020c0
女
  • 第一個(gè)即0x001d80010000256disa,先不管它挪略。
  • 我們可以看出agech1以及ch2并沒有打印出我們想要的18历帚、a、b杠娱,其實(shí)這里就是編譯器自身幫我們優(yōu)化的一個(gè)過程挽牢,那有沒有注意到0x0000001200006261打印結(jié)果呢?下面我們分開來打印一下看看:
(lldb) po 0x00000012
18
(lldb) po 0x62
98
(lldb) po 0x61
97

97摊求、98感覺不太對(duì)禽拔?那還記得ASSIC編碼嗎?

屬性以8字節(jié)對(duì)齊,其中isa固定占8字節(jié)睹栖,最后一共占40字節(jié)

3) 對(duì)象內(nèi)存對(duì)齊分析:

上一篇 說到obj = (id)calloc(1, size);進(jìn)一步的探索需要在libmalloc源碼中進(jìn)行硫惕,下面我們來具體看一下。

為什么傳入40野来,會(huì)打印出48呢恼除?

void *p = calloc(1, 40);
(lldb) po malloc_size(p)
48
  • 首先來到malloc_zone_calloc方法中,里面有一行至關(guān)重要的代碼ptr = zone->calloc(zone, num_items, size);初始化并且返回了一個(gè)ptr指針,斷點(diǎn)在此處打印如下:
(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $1 = 0x0000000100381a5f (.dylib`default_zone_calloc at malloc.c:249)
  • 然后來到default_zone_calloc中梁只,會(huì)看到return zone->calloc(zone, num_items, size);斷點(diǎn)在此處繼續(xù)打印如下:
(lldb) p zone->calloc
(void *(*)(_malloc_zone_t *, size_t, size_t)) $2 = 0x0000000100383042 (.dylib`nano_calloc at nano_malloc.c:884)
  • 接著來到nano_calloc中缚柳,這個(gè)方法中核心代碼是void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);,進(jìn)行內(nèi)存申請(qǐng)搪锣,并且返回指針p秋忙。

  • _nano_malloc_check_clear 中找到這行size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key);就是對(duì)象開啟內(nèi)存大小的算法:

#define SHIFT_NANO_QUANTUM      4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM)   // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
    // size = 40
    size_t k, slot_bytes;

    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
    // (size + 1<<4 -1) >> 4
    k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
    slot_bytes = k << SHIFT_NANO_QUANTUM;                           
    *pKey = k - 1;                                                  
    return slot_bytes;
}

以上我們可以看出對(duì)象是16字節(jié)對(duì)齊,避免發(fā)生內(nèi)存溢出/野指針的問題构舟。

四灰追、總結(jié)

1、屬性是以8字節(jié)對(duì)齊狗超,對(duì)象是以16字節(jié)對(duì)齊弹澎。
2、isa本身會(huì)占8字節(jié)努咐。
3苦蒿、聲明成員變量的順序不一致,會(huì)導(dǎo)致最終分配內(nèi)存大小的不同渗稍。

如有不當(dāng)佩迟,歡迎指正,感謝竿屹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末报强,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拱燃,更是在濱河造成了極大的恐慌秉溉,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碗誉,死亡現(xiàn)場(chǎng)離奇詭異召嘶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)哮缺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門苍蔬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蝴蜓,你說我怎么就攤上這事。” “怎么了茎匠?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵格仲,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我诵冒,道長(zhǎng)凯肋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任汽馋,我火速辦了婚禮侮东,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘豹芯。我一直安慰自己悄雅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布铁蹈。 她就那樣靜靜地躺著宽闲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪握牧。 梳的紋絲不亂的頭發(fā)上容诬,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音沿腰,去河邊找鬼览徒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛颂龙,可吹牛的內(nèi)容都是我干的习蓬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼厘托,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼友雳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铅匹,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤押赊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后包斑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體流礁,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年罗丰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了神帅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萌抵,死狀恐怖找御,靈堂內(nèi)的尸體忽然破棺而出元镀,到底是詐尸還是另有隱情,我是刑警寧澤霎桅,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布栖疑,位于F島的核電站,受9級(jí)特大地震影響滔驶,放射性物質(zhì)發(fā)生泄漏遇革。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一揭糕、第九天 我趴在偏房一處隱蔽的房頂上張望萝快。 院中可真熱鬧,春花似錦著角、人聲如沸揪漩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氢拥。三九已至,卻和暖如春锨侯,著一層夾襖步出監(jiān)牢的瞬間嫩海,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工囚痴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叁怪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓深滚,卻偏偏與公主長(zhǎng)得像奕谭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痴荐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354