iOS底層之內(nèi)存對(duì)齊

一、什么是內(nèi)存對(duì)齊?

內(nèi)存對(duì)齊是一種在計(jì)算機(jī)內(nèi)存中排列數(shù)據(jù)(表現(xiàn)為變量的地址)、訪問數(shù)據(jù)(表現(xiàn)為CPU讀取數(shù)據(jù))的一種方式。

它包含了兩種相互獨(dú)立又相互關(guān)聯(lián)的部分:基本數(shù)據(jù)對(duì)齊結(jié)構(gòu)體數(shù)據(jù)對(duì)齊

二、為什么要進(jìn)行內(nèi)存對(duì)齊?

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

說的通俗點(diǎn)就是方便讀取,速度快。

三、內(nèi)存對(duì)齊原則:

1、數(shù)據(jù)成員對(duì)?規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)數(shù)據(jù)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數(shù)組,結(jié)構(gòu)體等)的整數(shù)倍開始(比如int為4字節(jié),則要從4的整數(shù)倍地址開始存儲(chǔ))。下面咱們用(m, n)來理解一下,其中m為內(nèi)存的起始位置n為當(dāng)前成員的內(nèi)存大小,m,n一定要滿足m%n==0。

2、結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有某些結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小整數(shù)倍地址開始存儲(chǔ)(struct a里存有struct b,b里有char、int 、double等元素,那b應(yīng)該從8的整數(shù)倍開始存儲(chǔ)。)

3、收尾工作:結(jié)構(gòu)體的總大小,也就是sizeof的結(jié)果,必須是其內(nèi)部最大成員整數(shù)倍,不足的要補(bǔ)?。

各位同學(xué),能理解上面這些原則嗎,反正光看文字我是理解不了,沒關(guān)系,咱們是程序員,咱們可以用代碼來解釋。

四、數(shù)據(jù)成員結(jié)構(gòu)體內(nèi)存分析

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

struct Struct2 {
    double a;   //8
    int b;      //4
    char c;     //1
    short d;    //2
}struct2;
NSLog(@"結(jié)果為:%lu-%lu",sizeof(struct1),sizeof(struct2));

咱們來看下打印結(jié)果

 結(jié)果為:24-16

為什么會(huì)出現(xiàn)不同的結(jié)果呢,咱們來分析一下,不過在分析代碼之前呢咱們先看下各個(gè)類型的內(nèi)存大小

數(shù)據(jù)類型內(nèi)存大小表

下面咱們來分析一下Struct1的結(jié)果:

  • 首先Struct1中以double開始,在內(nèi)存中的地址是0~7
  • 接下來是char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址8,此時(shí)8%1==0表示可以從此位置開始存放char,在內(nèi)存中的地址是8。
  • 然后是int,int占用內(nèi)存大小為4字節(jié),此時(shí)在內(nèi)存中的起始位置9,但是現(xiàn)在9%4 != 0,所以此時(shí)只能向后移,找到一個(gè)能被4整除的數(shù),下一個(gè)能被4整除的數(shù)是12,所以此時(shí)int的起始位置為12,長(zhǎng)度為4字節(jié),在內(nèi)存中的地址是12~15。
  • 最后是shortshort占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置1616%2==0成立,所以short在內(nèi)存中的起始位置是16,長(zhǎng)度為2,在內(nèi)存中的地址是16~17。
    由上述結(jié)果可得Struct1在內(nèi)存中占用18個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊第三條原則,Struct1內(nèi)部最大成員為double8字節(jié),所以Struct1最終占用的內(nèi)存大小為24字節(jié)。由此可以得出下圖:
    Struct1內(nèi)存分布圖

同理,我們?cè)賮矸治鲆幌?code>Struct2的結(jié)果:

  • 首先Struct2中以double開始,在內(nèi)存中的地址是0~7
  • 然后是int,int占用內(nèi)存大小為4字節(jié),此時(shí)在內(nèi)存中的起始位置8,8%4 == 0等式成立,所以此時(shí)int的起始位置為8,長(zhǎng)度為4字節(jié),在內(nèi)存中的地址是8~11
  • 接下來是char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址12,此時(shí)12%1==0等式成立表示可以從此位置開始存放char,在內(nèi)存中的地址是12。
  • 最后是short,short占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置1313%2==0等式不成立,需要向后移,找到一個(gè)能被2整除的數(shù),所以short在內(nèi)存中的起始位置是14,長(zhǎng)度為2,在內(nèi)存中的地址是14~15。
    有上述結(jié)果可得Struct2在內(nèi)存中占用16個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊原則第三條,Struct2內(nèi)部最大成員為double8字節(jié),所以Struct2最終占用的內(nèi)存大小為16字節(jié)。由此可得出下圖:
    Struct2內(nèi)存分布圖

這就是Struct1Struct2結(jié)果不同的原因。
由此我們可以得出結(jié)論:結(jié)構(gòu)體所占內(nèi)存大小與結(jié)構(gòu)體內(nèi)部的成員變量的順序有關(guān)

五、嵌套結(jié)構(gòu)體內(nèi)存分析

struct Struct3 {
    double a;              //8
    char b;                //4
    struct Struct1 c;      //24
    short d;               //2
}struct3;

struct Struct4 {
    double a;              //8
    char b;                //1
    short c;               //2
    struct Struct2 d;      //16
}struct4;
NSLog(@"結(jié)果為:%lu-%lu",sizeof(struct3),sizeof(struct4));

咱們來看下打印結(jié)果

結(jié)果為:48-32

我們來分析下Struct3的結(jié)果:

  • 首先Struct3中以double開始,在內(nèi)存中的地址是0~7。
  • 接下來是charchar占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址8,此時(shí)8%1==0等式成立表示可以從此位置開始存放char,在內(nèi)存中的地址是8,在內(nèi)存中的地址是8。
  • 然后是結(jié)構(gòu)體Struct1,由上面的結(jié)論得知Struct1的大小為24字節(jié),此時(shí)在內(nèi)存中的起始地址9,根據(jù)內(nèi)存對(duì)齊原則第二條得知需從其內(nèi)部最大元素大小整數(shù)倍地址開始存儲(chǔ),Struct1中最大的為double8字節(jié),9%8 == 0不成立,所以取最近的一個(gè)能被8整除的數(shù)為16,所以Struct1在內(nèi)存中的起始位置為16,長(zhǎng)度為24,在內(nèi)存中的地址是16~39
  • 最后是short,short占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置40,40%2==0等式成立,起始位置是40,長(zhǎng)度為2,在內(nèi)存中的地址是40~41
    有上述結(jié)果可得Struct3在內(nèi)存中占用42個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊原則,Struct3內(nèi)部包括子成員Struct1內(nèi)部最大成員為double8字節(jié),所以Struct3最終占用的內(nèi)存大小為48字節(jié)。由此可得出下圖:
    Struct3內(nèi)存分布圖

我們來分析下Struct4的結(jié)果:

  • 首先Struct4中以double開始,在內(nèi)存中的地址是0~7。
  • 接下來是char,char占用內(nèi)存大小為1字節(jié),此時(shí)在內(nèi)存中的起始地址8,此時(shí)8%1==0等式成立表示可以從此位置開始存放char,在內(nèi)存中的地址是8,在內(nèi)存中的地址是8。
  • 然后是shortshort占用內(nèi)存2字節(jié),此時(shí)內(nèi)存中的起始位置9,9%2==0等式不成立,其后能被2整除的數(shù)為10,那么起始位置是10,長(zhǎng)度為2,在內(nèi)存中的地址是10~11。
  • 最后是結(jié)構(gòu)體Struct2,由上面的結(jié)論得知Struct2的大小為16字節(jié),此時(shí)在內(nèi)存中的起始地址12,根據(jù)內(nèi)存對(duì)齊原則第二條得知需從其內(nèi)部最大元素大小整數(shù)倍地址開始存儲(chǔ),Struct2中最大的為double8字節(jié),12%8 == 0不成立,所以取最近的一個(gè)能被8整除的數(shù)為16,所以Struct2在內(nèi)存中的起始位置為16,長(zhǎng)度為16,在內(nèi)存中的地址是16~31。
    有上述結(jié)果可得Struct4在內(nèi)存中占用32個(gè)字節(jié),根據(jù)內(nèi)存對(duì)齊原則Struct4內(nèi)部包括子成員Struct1內(nèi)部最大成員為double8字節(jié),所以Struct4最終占用的內(nèi)存大小為32字節(jié)。由此可得出下圖:
    Struct4內(nèi)存分布圖

以上就是我對(duì)內(nèi)存對(duì)齊原則的理解,如果有不同意見的同學(xué)歡迎留言給我。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容