三 OC底層原理 內(nèi)存對齊詳解

由于上篇文章解析 探究 OC 對象創(chuàng)建過程 探索到一部分的內(nèi)存,我們粗略跳過激捏,這次我們就詳細(xì)看看

先提出一個(gè)疑問

//  引用   #import <objc/runtime.h> #import <malloc/malloc.h>

    TObject *obj1 = [TObject new];
    obj1.prString = @"TObject";
    obj1.prInt = 12;
    //
    NSLog(@"對象申請的內(nèi)存空間大凶餮獭: ?%lu",class_getInstanceSize([obj1 class]));
    NSLog(@"系統(tǒng)實(shí)際開辟的內(nèi)存空間:%lu",malloc_size((__bridge const void *)(obj1)));

===============
2020-12-08 19:53:15.497897+0800 OC[36634:1466242] 對象申請的內(nèi)存空間大锌弁簟: ?24
2020-12-08 19:53:15.498207+0800 OC[36634:1466242] 系統(tǒng)實(shí)際開辟的內(nèi)存空間:32

為什么對象申請的內(nèi)存是 24硝训,而系統(tǒng)開辟出來的確實(shí) 32呢?

首先我們看看 對象申請的內(nèi)存是24是怎么回事

size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
    return word_align(unalignedInstanceSize());
 }
// 獲取類所有屬性的內(nèi)存大小總和
uint32_t unalignedInstanceSize() const {
        ASSERT(isRealized());
        return data()->ro()->instanceSize;
    }
// 內(nèi)存對齊算法
static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}
#ifdef __LP64__
#   define WORD_SHIFT 3UL
#   define WORD_MASK 7UL
#   define WORD_BITS 64
#else
#   define WORD_SHIFT 2UL
#   define WORD_MASK 3UL
#   define WORD_BITS 32
#endif

我們主要看 word_align 方法

(x + WORD_MASK) & ~WORD_MASK 這是二進(jìn)制的非運(yùn)算

我們的 TObject 所有熟悉占用內(nèi)存如下
isa: 8字節(jié);prString 是  string類型 8字節(jié)铃辖; prInt 是 int 類型是 4字節(jié)
8 + 8 + 4 所以是 20 字節(jié)
所以 x + WORD_MASK 就是 27 字節(jié) 
--------------------------------------------
內(nèi)存對齊為 &運(yùn)算剩愧,需要轉(zhuǎn)化為二進(jìn)制來計(jì)算〗空叮可使用二進(jìn)制計(jì)算機(jī)計(jì)算
// 0001 1011  => 27
// & 表示 與
// 1111 1000 => ~7 表示7的非運(yùn)算 仁卷,注意 如果進(jìn)行& 運(yùn)算的話 就會將 二進(jìn)制后三位抹去,也就是8字節(jié)對齊
// 0001 1000 => 最終結(jié)果 24

這就說明 我們的對象申請的內(nèi)存空間是 8字節(jié)對齊的

malloc 的源代碼在 libmalloc
從上篇可知犬第,在創(chuàng)建 instanceSize 之后 調(diào)用了 calloc 之后我們就跟不進(jìn)去了
具體實(shí)現(xiàn)如下

calloc(size_t num_items, size_t size)
{
    void *retval;
    retval = malloc_zone_calloc(default_zone, num_items, size);
    if (retval == NULL) {
        errno = ENOMEM;
    }
    return retval;
}
void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
    MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);

    void *ptr;
    if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
        internal_check();
    }
        // 走到這里 就要陷入死循環(huán)了 肯定有問題的
    ptr = zone->calloc(zone, num_items, size);
    
    if (malloc_logger) {
        malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
                (uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
    }

    MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
    return ptr;
}

這里我們就只能 使用斷點(diǎn) 一步一步往下跟了
就跟到 nano_calloc -> _nano_malloc_check_clear ->segregated_size_to_fit

// 這個(gè)方法就是關(guān)鍵了
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
    size_t k, slot_bytes;
        // 當(dāng)size 為0的時(shí)候 ;給 size 賦值16
    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
        // size + 15 右移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;                           // multiply by power of two quanta size
    *pKey = k - 1;                                                  // Zero-based!

    return slot_bytes;
}

#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM)   // 16
#define SHIFT_NANO_QUANTUM      4

現(xiàn)在來詳細(xì)分析一下
按照上文 我們傳入的 size 是 24

k = (size + NANO_REGIME_QUANTA_SIZE - 1)  >>  SHIFT_NANO_QUANTUM
也就可以理解為
k = (24 + 16 -1) >> 4 // >> 是位移運(yùn)算
// 0010 0111  ==> 24+16-1 = 39 這是39的二進(jìn)制形式
右移四位
// 0000 0010 ==> 這就是此時(shí) k 的進(jìn)制
slot_bytes = k << SHIFT_NANO_QUANTUM;
// k 再左移四位
// 0010 0000 // 表示 32
最終返回 32 字節(jié)

系統(tǒng)通過位移運(yùn)算 截取了二進(jìn)制的 后四位锦积,也就是16字節(jié)對齊

通過位移運(yùn)算 我們得知 系統(tǒng)是16位對齊的
這也就是 對象申請 24字節(jié) 最終返回32 字節(jié)的原因了

所以我們可以知道 一個(gè)對象在系統(tǒng)占用的內(nèi)存是 16字節(jié)對齊的,并且最小是16字節(jié) (探究 OC 對象創(chuàng)建過程 中講到 最小返回16字節(jié))

內(nèi)存結(jié)構(gòu)

可知對象內(nèi)存中 確實(shí)是有一個(gè)isa的歉嗓,經(jīng)過內(nèi)存優(yōu)化丰介,內(nèi)存排列是盡可能的前部分占滿,最后16字節(jié)才是多出的部分


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鉴分,一起剝皮案震驚了整個(gè)濱河市哮幢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冠场,老刑警劉巖家浇,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碴裙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進(jìn)店門舔株,熙熙樓的掌柜王于貴愁眉苦臉地迎上來莺琳,“玉大人,你說我怎么就攤上這事载慈〔训龋” “怎么了?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵睦擂,是天一觀的道長模孩。 經(jīng)常有香客問我露懒,道長,這世上最難降的妖魔是什么秤茅? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮童叠,結(jié)果婚禮上框喳,老公的妹妹穿的比我還像新娘。我一直安慰自己厦坛,他們只是感情好五垮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杜秸,像睡著了一般放仗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撬碟,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天诞挨,我揣著相機(jī)與錄音,去河邊找鬼小作。 笑死亭姥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的顾稀。 我是一名探鬼主播达罗,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼静秆!你這毒婦竟也來了粮揉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抚笔,失蹤者是張志新(化名)和其女友劉穎扶认,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殊橙,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辐宾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年狱从,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叠纹。...
    茶點(diǎn)故事閱讀 39,739評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡季研,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出誉察,到底是詐尸還是另有隱情与涡,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布持偏,位于F島的核電站驼卖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鸿秆。R本人自食惡果不足惜酌畜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谬莹。 院中可真熱鬧檩奠,春花似錦、人聲如沸附帽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕉扮。三九已至整胃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喳钟,已是汗流浹背屁使。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奔则,地道東北人蛮寂。 一個(gè)月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像易茬,于是被迫代替她去往敵國和親酬蹋。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評論 2 354

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