OC底层研究2--内存对齐原理


本页所使用的objc runtime 756.2,来自GITHUB

开始继续学习研究OC源码,今天看的是OC的内存原理,以及对齐原理的分析。

1.概念

内存对齐在数据结构中,是比较基础也比较重要的一环,对于iOS开发,研究底层更免不了要了解他们的原理,方便我们更透彻的学习iOS的原理。

我们先来一段维基百科的数据结构对齐的相关知识。

内存对齐,也可以理解为数据结构对齐(Data structure alignment),是代码编译后在内存的布局与使用方式。包括三方面内容:数据对齐数据结构填充(padding)与包入(packing)。

下图是内存中各类型属性所占的空间大小:

列举一下各数据类型所占字节(32位):

  • A char (one byte) will be 1-byte aligned.
  • A short (two bytes) will be 2-byte aligned.
  • An int (four bytes) will be 4-byte aligned.
  • A long (four bytes) will be 4-byte aligned.
  • A float (four bytes) will be 4-byte aligned.
  • A double (eight bytes) will be 8-byte aligned on Windows and 4-byte aligned on Linux (8-byte with -malign-double compile time option).
  • A long long (eight bytes) will be 4-byte aligned.
  • A long double (ten bytes with C++Builder and DMC, eight bytes with Visual C++, twelve bytes with GCC) will be 8-byte aligned with C++Builder, 2-byte aligned with DMC, 8-byte aligned with Visual C++, and 4-byte aligned with GCC.
  • Any pointer (four bytes) will be 4-byte aligned. (e.g.: char, int)

需要注意的是,在64位机上,有以下不同

  • A long (eight bytes) will be 8-byte aligned.
  • A double (eight bytes) will be 8-byte aligned.
  • A long long (eight bytes) will be 8-byte aligned.
  • A long double (eight bytes with Visual C++, sixteen bytes with GCC) will be 8-byte aligned with Visual C++ and 16-byte aligned with GCC.
  • Any pointer (eight bytes) will be 8-byte aligned.

2. 原则

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

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

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

3. 实践

3.1 属性8字节对齐:

当创建对象时,其属性的按类型占据不同的内存空间,但是由于大小不一,需要进行补齐;原则则是以第一位补齐为8的倍数(64位系统是8为对齐,而32位系统是4位对齐)。

以下面的代码举例子

struct MyStruct {
    double a;
    char b;            // 第2行 char 在前 int 在后
    int c;
    short d;
} MyStr1;                

struct MyStruct2 {
    double a;
    int c;            // 第2行 int 在前 char 在后
    char b;
    short d;
} MyStr2;                

// 我们打印一下各自的所占空间

NSLog(@"%lu -- %lu",sizeof(MyStr1), sizeof(MyStr2));

结果如下:

为何两者不一致?

我们来看第一个结构体对象的内部结构,排第一的属性double占8位,而char需要补齐7位成为8位,而剩余的int,short 则分别占4位,不需补齐,可以灵活堆在同一个内存区域。

struct MyStruct {
    double a;            // 8位
    char b;                // 1位  + 7 补齐  = 8
    int c;                // 4位
    short d;            // 4位
} MyStr1;            

总计是 8 + 8 + 4 + 4 = 24;

而第二个结构体对象,排第一的属性double占8位,而剩余的int,short ,short 总计都,不需补齐,可以灵活堆在同一个内存区域。

struct MyStruct2 {
    double a;            // 8 
    int c;                // 4        
    char b;                // 1  补齐1位 
    short d;            // 2   
} MyStr2;    

这里后3位,可以放到一个8位内存区间,即4 + 1 + 2,只需补齐1位至8位,可以灵活排列在一个内存空间内,所以本结构体对象总占内存为8+4+1+2 + 1(补齐1位) = 16。

如果没有弄懂,我们找出源码来读一读:(/objc/Private Headers/Project Headers/objc-os.h)

  1. 创建对象 alloc

  2. alloc方法的实现——返回rootAlloc方法

  3. 返回callAlloc的实现

  4. 当缓存区没有对象时,创建对象

  5. 去内存区域创建对象

  6. 执行类的属性方法,确定创建的空间大小

  7. 对齐内存

  8. 内存对齐的返回:类的属性大小取决于指针大小

  9. 8字节对齐:实现方法

3.2 对象16字节对齐

OC 底层研究1–alloc和init原理 我们分析过,iOS环境下,为了容错处理,创建。

在 libmalloc/src/nano_malloc.c 第193行,我们发现如下的代码:

而其中NANO_REGIME_QUANTA_SIZE 为16,

SHIFT_NANO_QUANTUM 为4

那可以开始分析源码如下:

if (0 == size) {
    size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}

这里的意思是,如果size为0,即创建空对象,返回16位默认空间。

k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM

是向右移动4位,即16字节对齐,那究竟是怎么实现的,我们不妨来看下,假定给定申请的内存size 为40,那么

40(size) + 16 - 1 =  55
0011 0111                        // 55 的2进制显示
0000 0011                        // >> 右移动4位  == 3
0011 0000                        // 执行 slot_bytes = k << SHIFT_NANO_QUANTUM; 左移动4位
slot_bytes = 48(0011 0000)

er, 第205行中返回的slot_bytes 即是最终对齐后的内存空间位,即给入40,最终系统对齐后,返回48位(16的整数位)。

4 结论

在iOS环境下,我们创建的对象和指针遵循16位对齐的原则,而其属性位8字节对齐。


文章作者: 李佳
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 李佳 !
评论
 上一篇
OC底层研究3--isa的初始化和指向分析 OC底层研究3--isa的初始化和指向分析
本页所使用的objc runtime 756.2,来自GITHUB 开始继续学习研究OC源码,这次研究的是isa的初始化和指向分析。 1. 概念什么是isa看看苹果文档的介绍: isa A Pointer to the class d
2019-12-27 李佳
下一篇 
OC 底层研究1--alloc和init原理 OC 底层研究1--alloc和init原理
本页所使用的objc runtime 756.2,来自GITHUB 1. 题目开始学习研究OC源码,今天看的是对象的初始化,也就是alloc以及init的分析。 2. 附流程图如下 3. 内容3.1引子我们先看一段代码,Person类
2019-12-16 李佳
  目录