iOS中關(guān)于內(nèi)存對(duì)齊的探究

結(jié)構(gòu)體內(nèi)存對(duì)齊的三大原則

1、數(shù)據(jù)成員對(duì)齊規(guī)則:結(jié)構(gòu)(struct)(或聯(lián)合(union))的數(shù)據(jù)成員,第一個(gè)數(shù)據(jù)成員放在offset為0的地方,以后每個(gè)成員存儲(chǔ)的起始位置要從該成員大小或者成員的子成員大小的整數(shù)倍開(kāi)始存儲(chǔ)。

2、結(jié)構(gòu)體作為成員:如果一個(gè)結(jié)構(gòu)里有結(jié)構(gòu)體成員,則結(jié)構(gòu)體成員要從其內(nèi)部最大元素大小的整數(shù)倍地址開(kāi)始存儲(chǔ)。

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

結(jié)合例子做具體分析:

struct ELStruct1{

  double a;   //8        [0,7]

  char   b;   //1        [8]

  int    c;   //4 9...11 [12,15]

  short  d;   //2        [16,17] //取最大內(nèi)部成員8的整數(shù)倍8*3=24對(duì)齊

}struct1;

struct ELStruct2{

  double a;   //8        [0,7]

  int    b;   //4        [8,11]

  char   c;   //1        [12]

  short  d;   //2  13    [14,15] //取最大內(nèi)部成員8的整數(shù)倍8*2=16對(duì)齊

}struct2;

打印結(jié)果:

struct1--24

struct2--16

我們?cè)賮?lái)一個(gè)稍微復(fù)雜一點(diǎn)的分析

struct ELStruct4{//接上繼續(xù)分析

  double a;   //8 20...23 [24,31]

  char   b;   //1         [32]

  int    c;   //4 33...35 [36,39]

  short  d;   //2         [40,41] //取最大內(nèi)部成員8的整數(shù)倍8*6=48對(duì)齊

}struct4;



struct ELStruct1{

  double a;   //8        [0,7]

  char   b;   //1        [8]

  int    c;   //4 9...11 [12,15]

  short  d;   //2        [16,17] //取最大內(nèi)部成員8的整數(shù)倍8*3=24對(duì)齊

}struct1;

struct ELStruct2{

  double a;   //8        [0,7]

  int    b;   //4        [8,11]

  char   c;   //1        [12]

  short  d;   //2 13     [14,15] //取最大內(nèi)部成員8的整數(shù)倍8*2=16對(duì)齊

}struct2;



struct ELStruct3{

  double a;   //8        [0,7]

  int    b;   //4        [8,11]

  char   c;   //1        [12]

  short  d;   //2 13     [14,15]

  int    e;   //4        [16,19]

  struct ELStruct4 str;

}struct3;

打印結(jié)果:

struct1--24

struct2--16

struct3--48

我們繼續(xù)探究ELPerson

@interface ELPerson : NSObject



@property(nonatomic,copy)NSString *name;



@property(nonatomic,copy)NSString *nickName;



@property(nonatomic,assign)long age;



@property(nonatomic,assign)float height;



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

  @autoreleasepool {

    // insert code here...

    //NSLog(@"Hello, World!");

     

    ELPerson *per = [ELPerson alloc];



    per.name      = @"Eli";

    

    per.nickName  = @"Eli";



    per.age       = 18;

   

    per.height    = 180.6;

     

    NSLog(@"%@ - %lu - %lu - %lu",per,sizeof(per),class_getInstanceSize([ELPerson class]),malloc_size((__bridge const void*)(per)));

  }

  return 0;

}

打印結(jié)果如下:

2021-06-09 15:31:08.107634+0800 KCObjcBuild[70838:2643794]
<ELPerson: 0x10068a320> - 8 - 40 - 48
sizeof()探究

我們先看看為什么sizeof(per)打印8

per是一個(gè)對(duì)象,對(duì)象的本質(zhì)是一個(gè)指針地址,指針地址的大小就是8字節(jié)
class_getInstanceSize()探究

繼續(xù)分析class_getInstanceSize([ELPerson class])

ELPerson 有4個(gè)屬性分別是NSString,NSString,long,float

分別是8+8+8+4=28字節(jié),再加上isa指針8字節(jié)就是36字節(jié)

我們跟蹤一下看看class_getInstanceSize()內(nèi)部實(shí)現(xiàn),看看為什么打印出來(lái)40和我們分析的不一樣

size_t class_getInstanceSize(Class cls)

{

  if (!cls) return 0;

  return cls->alignedInstanceSize();

}

繼續(xù)跟蹤

  uint32_t alignedInstanceSize() const {

    return word_align(unalignedInstanceSize());

  }

8字節(jié)對(duì)齊

#  define WORD_MASK 7UL

static inline uint32_t word_align(uint32_t x) {

  return (x + WORD_MASK) & ~WORD_MASK;

}


看到這里我們就明白了,他在內(nèi)部進(jìn)行了8字節(jié)的內(nèi)存對(duì)齊算法,所以把36字節(jié)對(duì)齊為40字節(jié)返回給我們了。我們以word_align(3)為例,下面具體分析下算法

    3   <->   0000 0011     7   <->  0000 0111

     

    3+7 <->   0000 0011    ~7   <->  1111 1000

     

            + 0000 1111            & 0000 1010

        

    10  <->   0000 1010     8   <->  0000 1000

結(jié)果就是 word_align(3)=8跟常見(jiàn)的算法 (3 + 7) >> 3 << 3是一個(gè)效果,都是8字節(jié)對(duì)齊,要想16字節(jié)對(duì)齊就是(3 + 15) >> 4 << 4

malloc_size()打印原因探究

我們直接進(jìn)入源碼,繪制出流程圖如下所示

calloc

我們把segregated_size_to_fit()展開(kāi)分析一下

#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM)   // 16

#define SHIFT_NANO_QUANTUM      4

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;

}

主要代碼翻譯一下:

( size + 16 - 1 )>> 4 << 4

可以看到這個(gè)里面也是進(jìn)行了一個(gè)內(nèi)存對(duì)齊的操作, 并且是16位內(nèi)存對(duì)齊,和我們上面說(shuō)的方法一模一樣的。對(duì)40進(jìn)行16位對(duì)齊,結(jié)果就是48了

總結(jié):

對(duì)象內(nèi)部的成員變量,進(jìn)行8字節(jié)內(nèi)存對(duì)齊。對(duì)象本身是16字節(jié)對(duì)齊。

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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