底层探索 -- Alloc实现流程(二)

307 阅读4分钟

Alloc实现流程(一) 的试探开始,终于看到了一些底层实质性的源码,接演前文继续探究 instanceSize 的内部实现。

本文中心:

  1. Alloc该为实例对象开辟了多大内存?
    • 根据类对象 的大小,以16字节对齐原则计算出要开辟的大小。
  2. 类对象的大小是多少?因素是什么?如何怎么计算的?
    • 影响类大小的因素是其内部的成员/属性的数量,与方法无关。
    • 依照结构体内存对齐原则,其实OC层终归是以8字节对齐计算类的大小。
  3. 结构体的内存对齐原则。
    • 这个一句话概况不了,写下面了。 👇👇🏻👇🏼👇🏾👇🏿
  4. 简析8和16字节内存对齐的原因。
    • 浅显一说:空间换时间,优化读取速度,减少CUP消化,容错。

理论伺候

1.1、跟一下instanceSize()流程

执行calloc() 之前,先跟一下instanceSize,看看内部方法是如何进行开辟内存大小的内存对齐的。

img.png 再调用alloc之前, 会先执行realizeClassWithoutSwift对类对象的相关内存初始话,其中就包括了setFastInstanceSize,这就是取cache中类的大小。(这块会另起一篇,展开来说)

img.png fast 中关键代买就是align16(),根据_flag取到的类对象 大小,以16字节对齐原则计算出要开辟的大小。

补充一下图中同理算法(align16->objc-os.h):
本质就是 x 在加上15之后,>>3后舍去低位,<<3补位后看看高位能否达到 x 下一个16字节对齐的点,如果能到也就升上去了,达不到就还是原来的大小了。

举个🌰:

假如 x = 24, 此时 x 16字节对齐的阶梯为32:
首先:x = 24, 二进制为:01 1000
然后:x + 15, 二进制为:10 0111
左右位移后:二进制为:10 0000, 十进制:32
再假如:x = 40, 二进制:10 1000,算算是不是升了?

1.2、结构体内存对齐

这就是3条纯粹的理论,类大小的计算就是在结构体内存对齐原则的基础上,以8字节对齐原则计算的。

1. 单纯数据成员对齐

结构体(Struct)或联合(Union)的第一个数据成员 起始为0,之后每个数据成员 i 存放位置要从 i 本身字节大小整数倍的地址开始存。

2. 结构体嵌套对齐

结构体(A) 的数据成员中包含其他结构体(B) ,那成员结构体(B) 的存放位置要从其 (B) 内部最大元素的整数倍的位置开始存。

3.结构体总大小

结构体(A)总大小,必须是其(A)内部最大成员 (一般都是8) 的整数倍,不足要补齐。

我再举个大栗子,细说一下

比如原则1+3:

image.png Struct 和 StructOne 虽然内部成员的数据类型完全一样,但是依则计算万以后的大小缺不相同。

比如原则2+3:

image.png StructTwo 的成员中包含了 Struct,其中 Struct 中最大元素a占8个字节,那 Struct e 存放的位置就应该是从24开始。

实践伺候

1. 结构体内存对齐

验证一下我举的大🌰,直接把刚才的代码跑起来,看看输出结果是否和理论时猜测一致。

image.png 实践的输出验证里理论的正确性。

2.类的大小

类的大小依照结构体内存对齐原则计算,不过在OC层一个空白的类也会有一个isa指针,至少都是8个字节。
UIPerson里加了个char,此时 的大小应该是8+1,经过对齐算法后应该会变为16,看一下运行结果。

img.png 嗯~有图有真相,因为要区分操作系统,所以算法中用的是宏,64位中

#define WORD_MASK 7UL

3.影响类大小的因素

影响类大小的因素是其内部的成员/属性的数量,与方法无关。为了验证这个说法,设置三种情况来看看类大小的变化。

  1. 只有1个属性。
  2. 情况1 + 1个成员变量。
  3. 情况2 + 1个实例方法。

img.png.png 这...结果再这呢,不用细说了吧?

4. 实例对象Alloc了多少内存

找上面的UIPerson帮个忙,验证一下 实例对象alloc出内存大小是否根据类对象 的大小,以16字节对齐原则计算出的。

image.png 这波试探现在算是整闭环了,由instanceSize起由instanceSize终,中心—>理论—>实践,完美~

总结

对象的大小、内存对齐说清楚了,不过在试探过程中也引出了关于的相关内容、还有isa相关内容,等我另起一篇再更新(一定会的)。

资源汇总