OC對象的內存分配及內存對齊

對象是如何分配內存的塌鸯?對象是如何計算內存大小的呢岸蜗?對象內存分配跟什么有關?

代碼分析

sizeof() 計算一個變量或者類型的大兄刈铩(以字節(jié)為單位)
class_getInstanceSize 計算對象所需要的內存大小,結算結果遵循8字節(jié)對齊努隙,其實現(xiàn)源碼如下:

static inline uint32_t word_align(uint32_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

malloc_size 計算對象的實際大小,使用時需要引入頭文件

//沒有添加任何屬性
@interface LNPerson : NSObject
@end
@implementation LNPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
      Person *person = [LNPerson alloc];
      NSLog(@"%lu - %lu - %lu",sizeof(person),class_getInstanceSize([LNPerson class]),malloc_size((__bridge const void *)(person)));
    }
    return 0;
}

打印結果:8 - 8 - 16

//添加一個屬性
@interface LNPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LNPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        LNPerson *person = [LNPerson alloc];
        NSLog(@"打印結果: %lu - %lu - %lu",sizeof(person),class_getInstanceSize([LNPerson class]),malloc_size((__bridge const void *)(person)));
    }
    return 0;
}

打印結果: 8 - 16 - 16

//添加兩個屬性
@interface LNPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end

@implementation LNPerson
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LNPerson *person = [LNPerson alloc];
        NSLog(@"打印結果: %lu - %lu - %lu",sizeof(person),class_getInstanceSize([LNPerson class]),malloc_size((__bridge const void *)(person)));
    }
    return 0;
}

打印結果: 8 - 24 - 32
這里先附上一張數(shù)據(jù)類型占用內存大小的表格:

圖1.1 數(shù)據(jù)類型占用內存大小.png

由以上三分代碼運行結果可以知道對象的內存大小跟屬性有關(實際上是跟實例變量有關)球恤。代碼分析如下:

  • 這里面sizeof的結果一直沒變化,實際上他只是計算person這個指針的大小剃法,而這個指針是一直不變的碎捺,在OC里面對象指針的大小為8字節(jié)(其他數(shù)據(jù)類型占用內存大小可以參考圖1-1)路鹰。
  • 通過class_getInstanceSize打印結果可以看出隨著屬性的增多贷洲,開辟一個對象所需要的內存也在增加(這里面需要注意的是屬性age是int屬性收厨,如果按照對象所需要內存大小計算應該是8+8(name屬性大小)+ 4(age)= 20,可是為什么結果卻是24呢优构?實際上這是底層結構體的內存對齊遵循了8字節(jié)對齊的計算結果诵叁,關于結構體內存對齊可以參考相關資料。)钦椭;
  • malloc_size計算的是對象的實際內存大小拧额,我們看到,這跟class_getInstanceSize計算出來的大小不一致彪腔,這實際上是因為OC對象內存分配時遵循了16字節(jié)對齊原則侥锦,當對象所需內存小于16字節(jié)時,還是會分配16字節(jié)德挣;當對象所需內存大于16字節(jié)且大小剛好為16的倍數(shù)時恭垦,則按照所需大小分配;但是當對象所需內存大于16字節(jié)且大小不是16的倍數(shù)時格嗅,則分配大于所需內存大小的最小的16的倍數(shù)番挺。

什么是內存對齊

內存對齊”應該是編譯器的“管轄范圍”。編譯器為程序中的每個“數(shù)據(jù)單元”安排在適當?shù)奈恢蒙贤鸵础却鎸R意味將數(shù)據(jù)類型寫入到內存地址時是按照它們大小切割的玄柏。簡單說就是如果內存地址是n字節(jié)的倍數(shù),那么我們說這n字節(jié)是內存對齊的贴铜,注意粪摘,這里n是2的冪,說白了绍坝,內存地址正好放下n字節(jié)的倍數(shù)赶熟,兩者相除余數(shù)為零,正好整除陷嘴。例如映砖,從上面的代碼分析可以看出對象person所需要的實際大小為24字節(jié),而實際大小確是32字節(jié)灾挨,這是因為OC對象的內存是16字節(jié)對齊的邑退。

為什么需要內存對齊

那編譯器為什么要進行內存對齊呢?主要基于以下兩個原因:

  • 1劳澄、平臺原因(移植原因):
    不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的地技;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常秒拔。
  • 2莫矗、性能原因
    數(shù)據(jù)結構(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內存作谚,處理器需要作兩次內存訪問三娩;而對齊的內存訪問僅需要一次訪問。

內存對齊遵循什么樣的原則

1妹懒、數(shù)據(jù)成員對齊規(guī)則:結構(struct)(或聯(lián)合(union))的數(shù)據(jù)成員雀监,第一個數(shù)據(jù)成員放在offset為0的地方,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員大小或者成員的子成員大姓;!(只要成員有子成員会前,比如說數(shù)組,結構體等)的整數(shù)倍開始(比如int為4字節(jié)匾竿,則要從4的整數(shù)倍地址開始存儲瓦宜。min(當前開始的位置m n))m = 9, n= 4:9 10 11 12 ,12是4的整數(shù)倍岭妖,所以從12開始存儲
2临庇、結構體作為成員:如果一個結構體里有些結構體成員,則結構體成員要從其內部最大元素的整數(shù)倍地址開始存儲区转。(struct a里面存儲struct b苔巨, b里有char、int废离、 double等元素侄泽,那b應該從8的整數(shù)倍開始存儲。)
3蜻韭、收尾工作:結構體的總大小悼尾,也就是sizeof的結果,必須是其內部最大成員的整數(shù)倍肖方,不足的要補齊闺魏。
舉個例子:

// 64位
struct MyStruct1 {
    double a;//8字節(jié) a存儲為0~7位置共8位
    char b;//1字節(jié) 8是1的整數(shù)倍,所以b從8位置開始存儲俯画,共1位
    int c;// 4字節(jié) 前8位已被占用析桥,9不是4的倍數(shù),所以得往前找最近的4的倍數(shù)12艰垂,從12位開始存儲泡仗,共四位12~15
    short d;//2字節(jié) 16位剛好是2的倍數(shù),從16位開始存儲猜憎,共兩位16~17娩怎,可以推出struct1的所需大小為0~17共18位
}struct1;// 但是結構大小是其內部最大成員(這里的a,8字節(jié))的整數(shù)倍胰柑,因此最小為24

struct MyStruct2 {// 同上面的推理
    double a;//8 0~7
    int c;// 4 8是4的倍數(shù) 8~11
    char b;//1 12
    short d;//2 13不是2的整數(shù)倍截亦, 故14~15爬泥,所需大小位0~15共16位,實際大小應該為16
}struct2;

struct MyStruct3 {// 同上面的推理
    double a;//8 0~7
    int c;// 4 8是4的倍數(shù) 8~11
    char b;//1 12
    short d;//2 13不是2的整數(shù)倍崩瓤, 故14~15袍啡,所需大小15
//如果一個結構體里有些結構體成員,則結構體成員要從其內部最大元素的整數(shù)倍地址開始存儲谷遂。所以雖然這里myStruct2的大小為16字節(jié)葬馋,但是依然以8字節(jié)為倍數(shù)開始計算卖鲤,這里16剛好是8的整數(shù)倍肾扰,所以myStruct2從16位開始,大小16位蛋逾,所以應該是16~31位集晚,總共大小32位
    struct MyStruct2 myStruct2;//16
}struct3;// 32

打印這三個結構體:

    NSLog(@"struct1大小:%ld",sizeof(struct1));
    NSLog(@"struct2大星弧:%ld",sizeof(struct2));
    NSLog(@"struct3大型蛋巍:%ld",sizeof(struct3));

打印結果:

2021-07-25 17:12:57.383194+0800 MemoryAlignmentTest[8346:636413] struct1大小:24
2021-07-25 17:12:57.383265+0800 MemoryAlignmentTest[8346:636413] struct2大锌鞴场:16
2021-07-25 17:12:57.383295+0800 MemoryAlignmentTest[8346:636413] struct3大辛隆:32

結果是符合規(guī)則的。

備注:OC對象的底層結構是結構體姑丑,結構體大小內存分配遵循8字節(jié)對齊原則蛤签。但是OC對象在結構8字節(jié)對齊計算出的內存大小的基礎上遵循16字節(jié)對齊原則。這么做應該是出于兼容性栅哀、容錯的等方面的考慮震肮,給對象留點空間,避免擠滿留拾。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末戳晌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子痴柔,更是在濱河造成了極大的恐慌沦偎,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咳蔚,死亡現(xiàn)場離奇詭異豪嚎,居然都是意外死亡,警方通過查閱死者的電腦和手機屹篓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門疙渣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人堆巧,你說我怎么就攤上這事妄荔∑镁” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵啦租,是天一觀的道長哗伯。 經常有香客問我,道長篷角,這世上最難降的妖魔是什么焊刹? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮恳蹲,結果婚禮上虐块,老公的妹妹穿的比我還像新娘。我一直安慰自己嘉蕾,他們只是感情好贺奠,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著错忱,像睡著了一般儡率。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上以清,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天儿普,我揣著相機與錄音,去河邊找鬼掷倔。 笑死眉孩,一個胖子當著我的面吹牛,可吹牛的內容都是我干的今魔。 我是一名探鬼主播勺像,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼错森!你這毒婦竟也來了吟宦?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤涩维,失蹤者是張志新(化名)和其女友劉穎殃姓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓦阐,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡蜗侈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了睡蟋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踏幻。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖戳杀,靈堂內的尸體忽然破棺而出该面,到底是詐尸還是另有隱情夭苗,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布隔缀,位于F島的核電站题造,受9級特大地震影響,放射性物質發(fā)生泄漏猾瘸。R本人自食惡果不足惜界赔,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牵触。 院中可真熱鬧淮悼,春花似錦、人聲如沸荒吏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至婚温,卻和暖如春锡宋,著一層夾襖步出監(jiān)牢的瞬間儡湾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工执俩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徐钠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓役首,卻偏偏與公主長得像尝丐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衡奥,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容