結構體內存對齊

對象內存對齊

探討的問題

1.什么是內存對齊?
2.為什么要做內存對齊?
3.結構體內存對齊規則
4.源碼內存對齊算法

一.什么是內存對齊?

計算機內存都是以字節為單位劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但是實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它為4或8的倍數),這就是所謂的內存對齊。內存對齊是一種在計算機內存中排列數據(表現為變量的地址) 、訪問數據(表現為CPU讀取數據)的一種方式。
內存對齊包含了兩種相互獨立又相互關聯的部分:基本數據對齊和結構體數據對齊 。

二.為什么要做內存對齊?

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

三.結構體內存對齊規則

當我們定義一個 struct 的時候,它在內存中是怎么存儲的?占用了多少字節的內存空間呢?

//1、定義兩個結構體
struct Mystruct1{
    char a;     //1字節
    double b;   //8字節
    int c;      //4字節
    short d;    //2字節
}Mystruct1;

struct Mystruct2{
    double b;   //8字節
    int c;      //4字節
    short d;    //2字節
    char a;     //1字節
}Mystruct2;

//計算 結構體占用的內存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));

輸出 24-16

從打印結果看出一個問題,結構體中變量相同,只是順序不同,結果影響結構體占用內存大小,這就是iOS中內存對齊

內存對齊規則

1.數據成員對?規則:結構(struct)(或聯合(union))的數據成員,第
一個數據成員放在offset為0的地方,以后每個數據成員存儲的起始位置要
從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數組,
結構體等)的整數倍開始(比如int為4字節,則要從4的整數倍地址開始存
儲。 min(當前開始的位置m,n) m=9 n=4 9 10 11 12
2.結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從
其內部最大元素大小的整數倍地址開始存儲.(struct a里存有struct b,b
里有char,int ,double等元素,那b應該從8的整數倍開始存儲.)
3.收尾工作:結構體的總大小,也就是sizeof的結果,.必須是其內部最大
成員的整數倍.不足的要補?。

拿上面的兩個結構體舉例說明

struct Mystruct1{
    char a;     //1字節 從 0開始 1結束  min(0,1)
    double b;   //8字節 從 1開始 1不能整除8 從8開始 8到15 min(8,8)
    int c;      //4字節 從 16開始 能整除4 16到19
    short d;    //2字節 從 20開始 20能整除2 20到21
}Mystruct1;

最后 Mystruct1 中變量最大字節是8 Mystruct1 0-21 共22字節  22向上取整8的倍數 = 24, sizeof(Mystruct1)=24

struct Mystruct3{
    int a;              //4字節 min(0,4)--- (0,1,2,3)
    struct Mystruct4{  
     //從4開始,存儲開始位置必須是最大的整數倍(最大成員為8),min(4,8)不滿足, min(8,8)滿          足,從8開始存儲
        double b;       //8字節 min(8,8)  --- (8,9,10,11,12,13,14,15)
        short c;        //2字節,從16開始,min(6,2) -- (16,17,18)
        char d;          //1字節,從19開始,min(19,1) -- (19)
    }Mystruct4;
}Mystruct3;
 
Mystruct3 內存大小必須是 Mystruct4 中變量最大字節數即8的倍數 Mystruct3 0-19 共20個字節 20向上取整8的倍數=24

打印內存大小方法

sizeof 最終得到的結果是該數據類型占用空間的大小
class_getInstanceSize 獲取類的實例對象所占用的內存大小
malloc_size 獲取系統實際分配的內存大小

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        NSLog(@"objc對象類型占用的內存大小:%lu",sizeof(objc));
        NSLog(@"objc對象實際占用的內存大小:%lu",class_getInstanceSize([objc class]));
        NSLog(@"objc對象實際分配的內存大小:%lu",malloc_size((__bridge const void*)(objc)));
    }
    return 0;
}

打印 8 8 16

內存對齊算法

align16: 16字節對齊算法
static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

#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_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;
}

算法原理:k + 15 >> 4 << 4 ,其中 右移4 + 左移4相當于將后4位抹零,跟 k/16 * 16一樣 ,是16字節對齊算法,小于16就成0了

對于一個對象來說,其真正的對齊方式 是 8字節對齊,8字節對齊已經足夠滿足對象的需求了
apple系統為了防止一切的容錯,采用的是16字節對齊的內存,主要是因為采用8字節對齊時,兩個對象的內存會緊挨著,顯得比較緊湊,而16字節比較寬松,利于蘋果以后的擴展。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。