iOS 对象原理探索二- alloc内存开辟及对齐方式

496 阅读4分钟

ios对象原理探究一

上一节学习了alloc内存的创建,并完成了alloc基本流程的梳理,在这个过程中看到了这个方法

以上方法有三个关键点

  1. 所需内存的大小(size = cls->instanceSize(extraBytes))
  2. 如何开辟内存
  3. isa对象绑定

下面我们具体的探索ios底层中是如何开辟内存的,我们常说的8字节对齐,16字节对齐,内存对齐又是什么?

我们看一段源码现象:

打印结果:

上面两张图struct one 和 strucu two 我们只更改了参数的位置,为什么内存就会多出8个字节,在struct Three,我们添加了个结构体参数,为什么会占48字节?

我们把上面所展示的现象就称作“内存对齐

一、什么是内存对齐

内存对齐是一种在计算机内存中排列数据(表现为变量的地址)、访问数据(表现为CPU读取数据)的一种方式。

它包含了两种相互独立又相互关联的部分:基本数据对齐结构体数据对齐

二、为什么我们要内存对齐

1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

3、以空间换时间

三、内存对齐的原则

  • 如下所示:

  • 具体的分析如第二图注释所述.

  • CPU的存取原理

通常我们认为内存,由一个个的字节组成,但是CPU 并不是以字节为单位存取数据的。CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。每次内存存取都会产生一个固定的开销,减少内存存取次数将提升程序的性能。所以 CPU 一般会以 2/4/8/16/32 字节为单位来进行存取操作。我们将上述这些存取单位也就是块大小称为(memory access granularity)内存存取粒度。

具体的CPU存取方式可参考《CPU内存的存取原理

  • 常规的数据类型所需要的字节

四、源码跟进

我们在开始的时候提出了一个问题,内存在OC的底层到底是如何开辟的,下面我们以源码的方式进行分析:

断点打到

进入 malloc_zone_calloc 方法/calloc方法中

坏了,只能到这了...😭,😭

点击Xcode的方法列表,我们可以看到如上图的下拉菜单,如此我们可以看出这两个方法来自那个框架.

我们打开libmalloc源码,在main函数中写如下代码:

点击 calloc方法跟进

进入malloc_zone_calloc方法

再次点击calloc方法

怎么又进入到了calloc方法里面...😭😭😭😭死循环了...

不急打个断点:执行 po zone->calloc 命令

可以看打 此方法由 default_zone_calloc 在malloc.c的第331行

执行 po zone->calloc 命令:

可以看打 此方法由 nano_calloc 在nano_malloc.c的第878行

点击核心方法_nano_malloc_check_clear 继续跟进

进入核心方法segregated_size_to_fit 继续跟进

上图我们看到开辟内存的核心算法:

NANO_REGIME_QUANTA_SIZE 和SHIFT_NANO_QUANTUM代表是16位和4位

k赋值算法: size + 16 -1 右移4位, slot_bytes 右移4位 ,具体如下

  12 + 16 - 1 = 27
27: 0001 1011 向右移4位 
    0000 0001 在向左移4位
    0001 0000 结果10进制的数为16

此时我们得到的slot_bytes就为16,返回的也就是16的倍数

五、总结

  1. 上面我们从代码现象到源码分析了整个内存开辟的过程,了解了整个对象的对齐方式为16字节对齐

  2. 源码分析过程中我们遇到了“断流”,学会了如何解决此类问题.

  3. 在开始我们的三个问题中还有一个未解决,isa绑定

内存分析就写到这里,如果哪里有不对的地方,还希望大家指出!谢谢