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阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 1、内存对齐的原因 我们都知道计算机是以字节(Byte)为单位划分的,理论上来说CPU是可以访问任一编号的字节数据...
    风紧扯呼阅读 513评论 2 2
  • 前话: 在了解内存对齐之前先了解一下各数据类型在内存中的大小,目前我们比较常用的是64位系统,所以我们的研究对象统...
    sz_蓝天使者阅读 706评论 0 3
  • 一、代码 Demo 可以看到 Struct1、Struct2、Struct3 的成员变量的数据类型都是相同的,仅仅...
    和风细羽阅读 619评论 0 2
  • 今天晚上我拿出了一打纸,姐姐教我折了一个荷花。荷花如果能上颜色就好了,可是它是有图案的纸。有一个小篮它的中间可以放...
    tanhaitao阅读 156评论 0 0
  • 《红楼梦》是中国文学的巅峰之作,影响甚广,又有戏曲、电影、电视剧等各种表现形式,越剧版“天上掉下个林妹妹”很多人都...
    李不言桃不语阅读 497评论 0 0