iOS底層原理 02 : 結(jié)構(gòu)體內(nèi)存對齊原則

什么是內(nèi)存對齊

元素是按照定義順序一個一個放到內(nèi)存中去的零截,但并不是緊密排列的。從結(jié)構(gòu)體存儲的首地址開始休蟹,每個元素放置到內(nèi)存中時棍苹,它都會認(rèn)為內(nèi)存是按照自己的大形匏蕖(通常它為4或8)來劃分的,因此元素放置的位置一定會在自己寬度的整數(shù)倍上開始廊勃,這就是所謂的內(nèi)存對齊。

編譯器為程序中的每個“數(shù)據(jù)單元”安排在適當(dāng)?shù)奈恢蒙暇选語言允許你干預(yù)“內(nèi)存對齊”坡垫。如果你想了解更加底層的秘密,“內(nèi)存對齊”對你就不應(yīng)該再模糊了画侣。

下圖是結(jié)構(gòu)體在32bit和64bit環(huán)境下各基本數(shù)據(jù)類型所占的字節(jié)數(shù):

基本數(shù)據(jù)類型所占的字節(jié)數(shù).png

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

接下來我們定義兩個struct冰悠,通過打印它們的sizeof()來探索一下其對齊的規(guī)律

struct LGHStruct01 {
    long  a;
    char b;
    double c;
    int d;
    short e;
}struct01;


struct LGHStruct02 {
    long  a;
    double b;
    int c;
    short d;
    char e;
}struct02;

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    NSLog(@"%lu-%lu",sizeof(struct01),sizeof(struct02));
        
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

// 打印的結(jié)果是: 32-24

我們可以看到兩個結(jié)構(gòu)體其中定義的變量 以及變量類型都是一致的,唯一的區(qū)別是在于定義變量的順序不一致配乱,為什么struct01溉卓,struct02所占的內(nèi)存大小一個是32一個是24呢皮迟?接下來我們來了解一下內(nèi)存對齊原則。

內(nèi)存對齊規(guī)則

  • 原則一:數(shù)據(jù)成員對?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員桑寨,第?個數(shù)據(jù)成員放在offset為0的地?伏尼,以后每個數(shù)據(jù)成員存儲的起始位置要從該成員??或者成員的?成員??(只要該成員有?成員,?如說是數(shù)組尉尾,結(jié)構(gòu)體等)的整數(shù)倍開始(?如int為4字節(jié),則要從4的整數(shù)倍地址開始存
    儲爆阶。 min(當(dāng)前開始的位置m n) m = 9 n = 4 (9 10 11 12)
  • 原則二:結(jié)構(gòu)體作為成員:如果?個結(jié)構(gòu)?有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最?元素??的整數(shù)倍地址開始存儲.(struct struct03?存有struct struct01,struct01?有l(wèi)ong,char,int ,double等元素,那struct01應(yīng)該從8的整數(shù)倍開始存儲.)
  • 原則三:收尾?作:結(jié)構(gòu)體的總??,也就是sizeof的結(jié)果,.必須是當(dāng)前結(jié)構(gòu)體或者其嵌套的某個結(jié)構(gòu)體成員的最大成員大小的整數(shù)倍.不?的要補?。
根據(jù)對齊原則沙咏,我們具體來分析struct01 辨图、struct02結(jié)構(gòu)體:
結(jié)構(gòu)體MyStruct1 內(nèi)存大小計算:
  • 變量a:占8個字節(jié),從0開始肢藐,min(0,8),即放在下0-7存儲
  • 變量b:占1個字節(jié)故河,從8開始,min(8,1),8%1==0吆豹,即放在8存儲
  • 變量c:占 8 個字節(jié)鱼的,從 9 開始,min(9,8),9%8!=0,往后移直到min(16,8),所以從變量c放在16-23存儲
  • 變量d:占4字節(jié)瞻讽,從24開始鸳吸,min(24,4),24%4==0,即d放在24-27存儲
  • 變量e:占2字節(jié),從28開始速勇,min(28,2),28%2==0,即e放在28-29存儲

因此struct01需要的內(nèi)存大小是30字節(jié)[0-29],而LGHStruct01中最大變量的字節(jié)數(shù)為8晌砾,所以 LGHStruct01 實際的內(nèi)存大小必須是 8 的整數(shù)倍,30向上取整到32烦磁,主要是因為32是8的整數(shù)倍养匈,所以 sizeof(LGHStruct01) = 32
示意圖如下:

LGHStruct01.png

結(jié)構(gòu)體MyStruct02 內(nèi)存大小計算:
  • 變量a:占8個字節(jié),從0開始都伪,min(0,8),即放在下0-7存儲
  • 變量b:占8個字節(jié)呕乎,從8開始,min(8,8),8%8==0陨晶,即放在8-15存儲
  • 變量c:占 4 個字節(jié)猬仁,從 16 開始,min(16,4),16%4==0,所以變量c放在16-19存儲
  • 變量d:占2字節(jié)先誉,從20開始湿刽,min(20,2),20%2==0,即d放在20-21存儲
  • 變量e:占1字節(jié),從22開始褐耳,min(22,1),22%1==0,即e放在22存儲

因此struct01需要的內(nèi)存大小是23字節(jié)[0-22],而LGHStruct02中最大變量的字節(jié)數(shù)為8诈闺,所以 LGHStruct02 實際的內(nèi)存大小必須是 8 的整數(shù)倍,23向上取整到24铃芦,主要是因為24是8的整數(shù)倍雅镊,所以 sizeof(LGHStruct02) = 24
示意圖如下:

LGHStruct02.png

結(jié)構(gòu)體嵌套
struct LGHStruct03 {
    int a;
    char b;
    short c;
    float d;
    struct LGHStruct01 str01;
}struct03;
NSLog(@"%lu",sizeof(struct03));
// 打印結(jié)果是48

LGHStruct03里面嵌套了LGHStruct01襟雷,

  • 變量a:占4個字節(jié),從0開始仁烹,min(0,4),即放在下0-4存儲
  • 變量1:占1個字節(jié)耸弄,從4開始,min(4,1),4%1==0晃危,即放在4存儲
  • 變量c:占 2 個字節(jié)叙赚,從 5 開始,min(5,2),5%2!=0,向后移到min(6,2),6%2==0, 變量c放在6-7存儲
  • 變量d:占4字節(jié)僚饭,從8開始震叮,min(8,4),8%4==0,即d放在8-11存儲
  • 變量str01:占32字節(jié),但str01里面最大變量所占的字節(jié)數(shù)是8鳍鸵,從12開始苇瓣,min(12,8),12%8!=0,向后移到1616%8==0,即e放在16-47存儲

所以LGHStruct03所需的內(nèi)存是48偿乖,對齊字節(jié)數(shù)應(yīng)為當(dāng)前結(jié)構(gòu)體或者嵌套的某個結(jié)構(gòu)體成員的最大成員大小击罪,是LGHStruct01里面的long類型為8,所以對齊數(shù)為8贪薪,而48剛好是8的倍數(shù)媳禁,所以sizeof(struct03) = 48.
如下圖所示:

LGHStruct03.png

內(nèi)存優(yōu)化(屬性重排)
我們知道蘋果它對OC對象的屬性的存儲是進(jìn)行了重排的。下面我們定義一個LGHPerson類画切,來看看屬性在內(nèi)存中的存儲竣稽。

@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float hair;
@property (nonatomic, assign) short ID;
@property (nonatomic) char c1;
@property (nonatomic) char c2;
@property (nonatomic) char c3;
@property (nonatomic) char c4;
@property (nonatomic) char c5;
@end

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        LGPerson *person = [LGPerson alloc];
        person.name      = @"Cooci";
        person.nickName  = @"KC";
        person.age       = 18;
        person.height    = 170;
        person.hair = 100;
        person.ID = 10;
        person.c1        = 'a';
        person.c2        = 'b';
        person.c3        = 'c';
        person.c4        = 'd';
        person.c5        = 'e';
        NSLog(@"%lu - %@ - %lu",sizeof(float),person,malloc_size((__bridge const void *)person));
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

我們在NSLog這里打個斷點,接下來看看每個屬性具體的存放位置:


malloc_size.png

查看person對象.png

查看地址存放的內(nèi)容.png

屬性存放在內(nèi)存的示意圖.png

**總結(jié): **
蘋果對OC對象的存儲進(jìn)行重排霍弹,根據(jù)屬性所占的字節(jié)數(shù)的大小毫别,從小到大排序,先存儲所占字節(jié)數(shù)較少的屬性典格,再存儲所占字節(jié)數(shù)較少的屬性岛宦,從而減少padding(內(nèi)存占位符),達(dá)到內(nèi)存優(yōu)化的效果.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耍缴,一起剝皮案震驚了整個濱河市砾肺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌防嗡,老刑警劉巖变汪,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異本鸣,居然都是意外死亡疫衩,警方通過查閱死者的電腦和手機硅蹦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門荣德,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闷煤,“玉大人,你說我怎么就攤上這事涮瞻±鹉茫” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵署咽,是天一觀的道長近顷。 經(jīng)常有香客問我,道長宁否,這世上最難降的妖魔是什么窒升? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮慕匠,結(jié)果婚禮上饱须,老公的妹妹穿的比我還像新娘。我一直安慰自己台谊,他們只是感情好蓉媳,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著锅铅,像睡著了一般酪呻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盐须,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天玩荠,我揣著相機與錄音,去河邊找鬼丰歌。 笑死姨蟋,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的立帖。 我是一名探鬼主播眼溶,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晓勇!你這毒婦竟也來了堂飞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绑咱,失蹤者是張志新(化名)和其女友劉穎绰筛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體描融,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡铝噩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了窿克。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骏庸。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡毛甲,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出具被,到底是詐尸還是另有隱情玻募,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布一姿,位于F島的核電站七咧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏叮叹。R本人自食惡果不足惜艾栋,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛉顽。 院中可真熱鬧裹粤,春花似錦、人聲如沸蜂林。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽噪叙。三九已至矮锈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睁蕾,已是汗流浹背苞笨。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留子眶,地道東北人瀑凝。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像臭杰,于是被迫代替她去往敵國和親粤咪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359