OC 底层原理(2)- 对象原理(内存对齐与 calloc 分析)(随记)

749 阅读4分钟

一、内存对齐原理

先给出一段代码,并打印对象的存储空间内容

上图 98、97 为 'a'和'b'的 ascii 值

由 0x0000001200006261 可以引出内存优化---内存拼接和内存对齐。

内存对齐原则(借鉴大神的理论):

1:数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第 一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要 从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组, 结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存 储。

2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从 其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b 里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大 成员的整数倍.不足的要补⻬。

给出一段代码说明

上图打印不同的数据结构,数据结构内所存的数据内容一样,但排序不一样,数据结构所占的内存大小也不一致(---二进制重排接与内存对齐)。

二、对象申请内存 VS 系统开辟内存

获取内存空间大小的两个API:

class_getInstanceSize:传入一个类对象返回一个对象的实例至少申请的内存,等价于sizeof;对象里面的属性空间8字节对齐

malloc_size:返回系统实际分配的内存大小;系统开辟的空间16字节对齐

注:LGPerson 没有任何属性。

根据上图提出两个问题:

1)当类里没有任何属性的情况下,为什么class_getInstanceSize 获取的内存大小要比 malloc_size 获取到的大8?

先给出class_getInstanceSize的调用流程及最终源码 class_getInstanceSize -> alignedInstanceSize -> word_align

此图可看出对象里的属性为 8 字节对齐。

再看 alloc 计算 size 源码

由上图可知,当size小于16的,就要等于16,因为在还没有属性的时候就有个默认属性 isa ,因为isa是指针类型,所以申请的内存空间大小就是8 ,但系统在实际分配内存空间的时候,发现申请的内存空间小于16所以就将size赋值了16,所以系统实际分配的内存就是16.这就为什么两者会相差8的缘故。

2)为什么当类里有属性并且大于属性内存大于16字节的时候,class_getInstanceSize 获取的内存是8字节对齐,malloc_size 获取到内存是16字节对齐?

因为 alloc 创建对象并申请内存的时候的流程(关于alloc调用流程,OC 对象原理(1)有提到)会到达, _class_createInstanceFromZone 这个方法(下图)

上图红色方框标出的地方 calloc 就是初始化并申请内存的地方,想要知道第二个问题答案,就需要去看看 calloc 源码(第三部分)讲解

三、calloc 原理探索

1)先下载并配置 malloc 源码

2)追踪流程计算size并返回的地方

calloc调用流程:calloc -> malloc_zone_calloc -> default_zone_calloc -> nano_calloc -> _nano_malloc_check_clear -> segregated_size_to_fit

segregated_size_to_fit 返回了 slot_bytes,可以推断这就是确定计算size大小的算法了

其中 NANO_REGIME_QUANTA_SIZE 的定义见下图
根据上图可知 NANO_REGIME_QUANTA_SIZE 宏定义的值为 16.

上图就是得出slot_bytes结果的算法,假定 size 的值为 40 时,这时上图的算法就可以解释为: slot_bytes = (40 + 16 - 1) >> 4 << 4;这里 可能会觉得有些熟悉,因为上面有提到差不对的算法(8 + 8 - 1)>> 3 << 3 ,当时说的是 8 字节对齐,所以 (40 + 16 - 1) >> 4 << 4 其实 16 字节对齐。不知道为什么的同学可以根据 (8 + 8 - 1)>> 3 << 3 去计算一下,在计算的过程中应该就可以体会。综上所诉 40 的 16 字节对齐是结果就是48 ,即 slot_bytes = 48。 这就是系统开辟的内存为16字节的点。

现在就回答上面的第二个问题:因为前面的8字节对齐的参照物是对象里面的属性,而属性的类型最大也是8字节,所以属性内存大小8字节对齐并不会有什么问题。但是系统分配的内存大小为16字节对齐,是因为此时参考的因素是整个对象,要是8字节对齐就很大内存溢出的风险。