iOS底層探究 - 內存對齊

目錄
1:內存對齊的原因
2:內存對齊的規則
3:結構體內存分配演練以及在iOS中對象成員的內存分配探索

一 :內存對齊的原因

計算機內存是以字節(Byte)為單位劃分的,理論上CPU可以訪問任意編號的字節,但實際情況并非想象中的一個一個字節取出拼接的,而是根據自己的字長來獨處數據的。
我們都知道CPU的數據總線寬度決定了CPU對數據的吞吐量,例如:64位CPU一次可以處理64bit也就是8個字節的數據,32位一個道理,每次可以處理4個字節的數據。

以32位的CPU為例,實際尋址的步長為4個字節,也就是只對編號為 4 的倍數的內存尋址,例如 0、4、8、12、1000 等,而不會對編號為 1、3、11、1001 的內存尋址。如下圖所示:

內存對齊

這樣做可以實現最快速的方式尋址且不會遺漏一個字節,也不會重復尋址。

那么對于程序而言,一個變量的數據存儲范圍是在一個尋址步長范圍內的話,這樣一次尋址就可以讀取到變量的值,如果是超出了步長范圍內的數據存儲,就需要讀取兩次尋址再進行數據的拼接,效率明顯降低了。例如一個double類型的數據在內存中占據8個字節,如果地址是8,那么好辦,一次尋址就可以了,如果是20呢,那就需要進行兩次尋址了。這樣就產生了數據對齊的規則,也就是將數據盡量的存儲在一個步長內,避免跨步長的存儲,這就是內存對齊。在32位編譯環境下默認4字節對齊,在64位編譯環境下默認8字節對齊。

二 :內存對齊的規則

每個特定平臺上的編譯器都有自己的默認“對齊系數”(也叫對齊模數)。程序員可以通過預編譯命令#pragma pack(n)。在iOS平臺中默認的對齊系數是8

1、數據成員對齊規則:(Struct 或 union 的數據成員)第一個數據成員放在偏移為0的位置,以后每個成員的偏移為 min(對齊系數,自身長度)的整數倍,不夠整數倍的補齊。

2、數據成員為結構體:該數據成員的自身長度為其最大長度的整數倍開始存儲

3、整體對齊規則:數據成員按照上述規則對齊之后,其本身也要對齊,
對齊原則是min(對其系數,成員最大長度)的整數倍。

三 :結構體內存分配演練以及在iOS中對象成員的內存分配探索

我們用以下三個結構體做為例子去探索下內存對齊的規則:

struct Struct1 {
    char a;     // 1 + 7
    double b;   // 8
    int c;      // 4
    short d;    // 2 + 2
} MyStruct1;

struct Struct2 {
    int b;   // 0 7
    char a;     // 8
    int c;      // min(9 4) = 4
    short d;    // 2
    // 16 17 
} MyStruct2;

struct Struct3 {
    double b;   // 0 7
    int c;      // min(9 4) = 4
    char a;     // 8
    short d;    // 2
    // 16 17
} MyStruct3;

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"%lu-%lu-%lu",sizeof(MyStruct1),sizeof(MyStruct2),sizeof(MyStruct3));
    }
}

分別對他們求大小,在Struct1中:

 長度          對齊     偏移      區間
char   a;       1       0       [0]     
double b;       8       8       [8,  15]
int    c;       4       16      [16, 19]
short  d;       2       20      [20, 21]
    
1、數據成員的對齊按照#pragma pack-8和自身長度中比較小的那個進行
-- char a 的自身長度為 1, min(1,8) = 1, 按 1 對齊

2、第一個數據成員放在offset為0的地方
-- char a 的偏移為 0

3、整體對齊系數 = min((8,max(int,short,char,double)) = 8,
   將 21 提升到 8 的倍數,則為 24,所以最終結果為 24 個字節

在Struct2中:

 長度          對齊     偏移      區間
double   b;    8       0       [0,    7]     
char a;        1       8       [   8   ]
int    c;      4       12      [12,  15]
short  d;      2       16      [16,  17]
    
1、數據成員的對齊按照#pragma pack-8和自身長度中比較小的那個進行
-- double b 的自身長度為 8, min(8,8) = 8, 按 8 對齊

2、第一個數據成員放在offset為0的地方
-- double b 的偏移為 0

3、整體對齊系數 = min((8,max(int,short,char,double)) = 8,
   將 17 提升到 8 的倍數,則為 24,所以最終結果為 24 個字節

在Struct3中:

 長度          對齊     偏移      區間
double b;      8       0       [0,    7]     
int    c;      4       8       [8,   11]
char   a;      1       12      [12]
short  d;      2       14      [14,  15]
    
1、數據成員的對齊按照#pragma pack-8和自身長度中比較小的那個進行
-- double b 的自身長度為 8, min(8,8) = 8, 按 8 對齊

2、第一個數據成員放在offset為0的地方
-- double b 的偏移為 0

3、整體對齊系數 = min((8,max(int,short,char,double)) = 8,
   將 15 提升到 8 的倍數,則為 16,所以最終結果為 16 個字節

最后看下輸出結果和我們計算是否一致:

[88992:5534353] 24-24-16

我們可以看到struct2和struct3中相同的數據成員,不同的位置,促成了不同的內存分配結果,其原因就是因為我們的內存對齊規則導致的。

三 :在iOS中類對象的內存分配

我們先看一段代碼:

@interface LGTeacher : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic, strong) NSString *job;

@property (nonatomic, assign) int sex;
@property (nonatomic) char ch1;
@property (nonatomic) char ch2;
@end

 Person *person = [[Person alloc] init];
 NSLog(@"%lu - %lu",class_getInstanceSize([person class]),malloc_size((__bridge const void *)(person)));

我們創建了一個person對象,并對他的對象實例所占內存大小和系統為此對象開辟空間大小進行打印,得出結果:

[23426:8161973] 40 - 48

為什么對象本身大小和系統為對象分配空間不一致呢?我們根據alloc實現的底層源碼知道,對象是以8個字節對齊的,內存優化之后得到結果40我們可以理解,但是為什么系統要為我們多開辟8個字節的空間呢?
我們看下malloc的底層源碼實現:

我們跟蹤malloc的調用,最后發現這個函數:
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
    size_t k, slot_bytes;

    if (0 == size) {
        size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
    }
    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;
}

1: k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM;
2: slot_bytes = k << SHIFT_NANO_QUANTUM;
我們重點看下這兩行代碼,第一行我們打印size得到了40(也就是對象的大小),其中這兩個宏定義的值NANO_REGIME_QUANTA_SIZE = 16 , SHIFT_NANO_QUANTUM = 4;也就是 k = (40 + 16 - 1) >> 4; 結果右移了4位。然后第二行代碼又對上個結果左移了4位。看到這個是不是和alloc的的對象對齊算法很類似?右移之后再左移相當于抹去了二進制的最后四位,前面又加了一個(16-1)得到的結果是16的倍數也就是16字節對齊,最小大小為16字節)。

得出結論:對象內存的申請按照8字節對齊,不滿8字節按照8字節計算;但是實際上malloc實際開辟內存的時候,則是進行了16字節對齊,避免對象之間發生溢出和野指針的問題,所以當對象大小為40時,后面要補8位,最后結果是48

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 1、內存對齊的原因 我們都知道計算機是以字節(Byte)為單位劃分的,理論上來說CPU是可以訪問任一編號的字節數據...
    風緊扯呼閱讀 523評論 2 2
  • 前話: 在了解內存對齊之前先了解一下各數據類型在內存中的大小,目前我們比較常用的是64位系統,所以我們的研究對象統...
    sz_藍天使者閱讀 719評論 0 3
  • 一、代碼 Demo 可以看到 Struct1、Struct2、Struct3 的成員變量的數據類型都是相同的,僅僅...
    和風細羽閱讀 654評論 0 2
  • 今天晚上我拿出了一打紙,姐姐教我折了一個荷花。荷花如果能上顏色就好了,可是它是有圖案的紙。有一個小籃它的中間可以放...
    tanhaitao閱讀 165評論 0 0
  • 《紅樓夢》是中國文學的巔峰之作,影響甚廣,又有戲曲、電影、電視劇等各種表現形式,越劇版“天上掉下個林妹妹”很多人都...
    李不言桃不語閱讀 521評論 0 0