iOS 内存对齐原理分析

368 阅读5分钟

开篇

好好学习,不急不躁。

上一节课,学习 alloc的开辟原理,粗浅的书写了一篇文章,记录个人所悟。这篇文章,延续上一文章的思路,继续对iOS开辟对象,以及对象内存进行学习与总结。

上一篇文章:iOS alloc 源码探究

内存

  • 内存,是所有一切程序运行的基础。每一部手机,每一款应用,所拥有和所能占用的内存都是有限制的。当程序占用内存过多时,系统就会报出警告,回收内存,这时候就容易出现程序崩溃闪退,这就会影响用户体验。所以一款好的程序,内存的考量是重中之重。
  • 所以我们需要对内存进行合理的分配,清除。但是在如何对内存进行分配与清除,这就需要我们对内存是如何创造,如何运用的有一定的认识;今天先通过一些简单的例子,总结一些内存方面的知识。

一、对象内存大小

1、 测试内存大小影响因素

创建一个对象,给对象添加几个属性,并且赋值,通过打印对象内存,可以查看对象内存大小;如下图,QLYPerson 内,所占用的内存大小为40

截屏2021-06-13 下午9.57.07.png

接下来分析QLYPerson 内存占用的原理;尝试删除一部分属性,查看QLYPerson所占用是否会改变?

24.png

结论:通过程序分析,可以看出,当QLYPerson对象内,只有两个属性时,所占内存大小为24,比上一次小了。由此可以暂时看出,属性会影响对象的内存大小

在一个对象中,不仅用到属性,还有变量,和方法。对此,我们都做一下测试;

变量.png

在 QLYPerson 内部声明并实现一个方法,查看QLYPerson大小是否有变化;

方法.png

结论:通过上图,我们可以清晰看出,对象内部不论是类方法,还是实例方法,并不会改变对象的内存大小。也可以看出,对象内的方法并不占用对象的内存大小的。

2、结论

结论:在 QLYPerson 内添加一个变量后, QLYPerson 的内存大小增加了,由此,可以看出,变量也会影响对象的内存大小

总结:通过实践,我们发现,对象内的属性变量,都会影响对象的内存大小。 所以日后声明属性和变量时,学会避免声明无用的属性变量

二、对象内存地址

介绍了影响内存大小的方式,接下来我们分析内存的对齐原则;

首先我们查看一下,数据在对象内部是如何分配的,通过x/8gx,打印出对象的内存分布。

11.png 如图所示,占满8字节的成员变量,我们很容易就能查看结果,可是对于不满8字节的,我们就没办法通过8字节的内存地址,直接查看结果。 由此,我们引进了一张字节位图,方便查看不同类型的成员变量,所占用的字节大小

字节位图.png 通过字节位图,看出 chatr 类型,占用2个字节,所以,q1、q2 各占2字节,从打印的内存地址可以看出,第二个地址,77,71 就是q1、q2 的内存地址;而CGFloat(64位) 占8位字节,所以 heigh 内存地址为:0x4066800000000000:

乱码.png

其实打印浮点型数据,我们可以通过 p/f 或者是 e -f f -- 来打印数据。如下图:

浮点.png

相信大家都比较好奇,为什么你就知道这个地址0x0000000100974048,代表的值是笑笑;这个0x0000000100974028 地址代表的值是 榴莲呢?

在运行的过程中,我们发现,age,q1,q2这三个成员变量的地址,是混在一起的。这又是为什么呢?

接下里,我们通过内存对齐原则,给大家解释一下;

三、结构体内存对齐

1、内存对有三大原则:

1、数据成员对齐规则:结构体(联合体)的数据成员,第一个数据成员放在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的结构,必须是其内部最大成员的整数倍,不足的要补齐

1.png 2.png

由上图可知,当结构体成员的位置不一样时,就会影响结构体总大小。

可为什么在struct1中,9,10,11,号位置不能存放数据呢?

2、字节对齐的好处

不能存.png

在日常开发中,是不是需要咱们开发者,时刻关注成员变量的顺序,对内存的影响呢?其实在iOS 底层,苹果开发已经帮我们做好了这方面的处理。比如上面的,age,q1,q2这三个成员变量的顺序是可以变化的,但不论怎么变化,他们的地址,都是混在一起的。这就是苹果底层帮我们做好的优化。

四、alloc流程补充

上篇文章,讲述了alloc开辟流程,alloc开辟过程中,执行 obj = (id)calloc(1, size); 向系统申请开辟空间,本文补充一下 calloc(1,size)开辟流程;

我们通过 malloc源码分析 calloc 底层

1.png

2.png

3.png

4.png

1.png