iOS之底层内存对齐

引言

内存对齐是内存里面一个很重要的词汇,可是大部分开发者对这个词汇的含义都是一知半解。

 WJPerson*wj = [WJPerson alloc];
wj.name = @"无极";
wj.age = 30;
NSLog(@"对象类型的内存大小:%lu",sizeof(wj));
NSLog(@"对象实际的内存大小:%lu",class_getInstanceSize([wj class]));
NSLog(@"对象分配的内存大小:%lu",malloc_size((__bridge const void *)(wj)));
NSLog(@"-----------------------------------------");

WJPerson*wj2;
NSLog(@"对象类型的内存大小:%lu",sizeof(wj2));
NSLog(@"对象实际的内存大小:%lu",class_getInstanceSize([wj2 class]));
NSLog(@"对象分配的内存大小:%lu",malloc_size((__bridge const void *)(wj2)));

输出结果:

2021-06-16 13:12:08.712404+0800 内存对齐[3440:72350] 对象类型的内存大小:8
2021-06-16 13:12:08.712537+0800 内存对齐[3440:72350] 对象实际的内存大小:24
2021-06-16 13:12:08.712659+0800 内存对齐[3440:72350] 对象分配的内存大小:32
2021-06-16 13:12:08.712746+0800 内存对齐[3440:72350] -----------------------------------------
2021-06-16 13:12:08.712831+0800 内存对齐      [3440:72350] 对象类型的内存大小:8
2021-06-16 13:12:08.712923+0800 内存对齐[3440:72350] 对象实际的内存大小:0
2021-06-16 13:12:08.713007+0800 内存对齐[3440:72350] 对象分配的内存大小:0

结果分析:

  • sizeof:对象类型的内存大小,sizeof是用来计算一个变量或者一个常量、一种数据类型所占的内存字节数。自定义对象的本质是结构体指针,所以占8个字节。
  • class_getInstanceSize:对象实际(对齐后)的内存大小,内存大小是由类的成员变量的大小决定的。实际上并不是严格意义上的对象的内存的大小,因为内存进行了8字节对齐,所以wj的内存大小是24而不是20。而wj2只是声明变量,并没有走alloc方法开辟内存,所以大小是0。核心内存大小算法是:define WORD_MASK 7UL ((x + WORD_MASK) & ~WORD_MASK
  • malloc_size:系统实际分配的内存大小,以16字节对齐,不足16的自动补齐。注意:系统的16字节对齐是在实际的内存大小(经过8字节对齐后)的基础上。上面的wj对象实际内存大小24字节,不是16的倍数,所以系统实际分配为32

问题:class_getInstanceSizemalloc_size 底层做了什么?我们如何知道class_getInstanceSize8字节对齐,而malloc_size16字节对齐?

在研究后面重点之前,我们先来看下基本数据类型在arm64环境下占用的内存大小。

基本数据类型所占字节数.gif

下面解释为什么计算机会有内存对齐的概念,出于什么目的要内存对齐。

  • 内存是以字节为基本单位,cpu在读取数据时,是以为单位读取,并不是以字节为单位读取。频繁读取未对齐的数据,会加大cpu的开销。字节对齐后,会降低cpu的存取次数,这种以空间时间的做法降低了cpu的开销。
  • cpu存取:是以为单位,存取未对齐的数据可能开始在上一个内存块,结束在另一个内存块。这样中间可能要经过复杂的运算在合并在一起,降低了效率,字节对齐后,提高了cpu的访问效率。

内存对齐规则:

数据成员对齐规则:结构体(struct)(或联合体(union))的数据成员,第一个数据成员放在offset为0的地方(即首地址的位置),以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int4字节),则要从4的整数倍地址开始存储。
结构体作为成员变量:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍开始存储(struct a里有struct b,b里有char,int,double等元素,那b应该从8(doudle为8 )整数倍开始存储)

下面我们先来看一个例子:


struct内存.gif
struct WJPerson1{
double a;       
char b;       
int c;          
short d;
}myPerson1;
struct WJPerson2
{
double b;
int c;
char a;
short d;
}myPerson2;
NSLog(@"%lu-%lu",sizeof(myPerson1),sizeof(myPerson2));

输出结果:

 2021-06-16 16:13:21.621334+0800 内存对齐[4074:158189] 24-16

从上面我们可以看出,myPerson1myPerson2两个结构体里面元素是一样的,只是顺序不同,内存大小却不一样,为什么?这就是结构体内存对齐。

具体分析如下:[p,q] p表示当前开始的位置,q表示大小
myPerson1:

  • double a: [0,7] 即(0~7存放a)
  • char b:[8,1] 即(8存放b)
  • int c:[12,15] 即(9,10,11不是int 4得出倍数,废弃,12~15存放c)
  • short d:[16,2] 即(16 ~ 17存放d)

myPerson2:

  • double a: (0,7) 即(0~7存放a)
  • int b:(8,4) 即(8~11存放b)
  • char c:(12,1) 即(12存放c)
  • short d:(14,2) 即(13不是d( short 2的倍数)位置废弃,14 ~ 15存放d)

下面这个是嵌套的结构体
struct WJPerson3 {
double a;
int b;
char c;
short d;
int e;
struct WJPerson1 str;
} myPerson3;


struct嵌套内存.gif

myPerson3具体分析如下:

  • double a: [0,7] 即(0~7存放a)
  • char b:[8,1] 即(8存放b)
  • int c:[12,15] 即(9,10,11不是int 4得出倍数,废弃,12~15存放c)
  • short d:[16,2] 即(16 ~ 17存放d)
  • int e:(20,4) 即(18,19不是int 4得出倍数,废弃.20 ~ 23存放d)
  • 变量str: str是结构体变量,内存对齐原则结构体成员要从其内部最大元素大小的整数倍地址开始存储。WJPerson1 中的最大的变量a( double)占8字节,所以offset从24开始,WJPerson1的内存大小是18字节。[24,18],即24 ~ 42存放 str,计算出来的是42个字节,但是myPerson3中最大的变量是str和 a都是 8 字节,所以myPerson3的实际内存大小必须是8的整数倍,42不是8的整数倍,因此补齐应该是48.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容