iOS 底层之内存对齐

5,961 阅读4分钟

序言

数据类型都有固定的内存大小,在结构体当中属性的内存排序遵循内存对齐原则,那么内存对齐的原理是怎么回事,内存对齐有什么好处?在OC中是怎么实现内存对齐的呢?

这里是不同数据类型的字节数

image.png

内存对⻬的原则

  1. 数据成员对⻬规则
    结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储。
  2. 结构体作为成员
    如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
  3. 收尾工作
    结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补⻬。

实例

代码

先来看这三个结构体的内存大小

struct LGStruct1 {
    double a;       //8
    char b;         //1
    int c;          //4
    short d;        //2
}struct1;

struct LGStruct2 {
    double a;       //8
    int b;          //4
    char c;         //1
    short d;        //2
}struct2;

struct LGStruct3 {
    double a;      //8
    int b;         //4
    char c;        //1
    short d;       //2
    int e;         //4
    struct LGStruct1 str; //
}struct3;

通过sizeof获取到struct1的内存是24,struct2的内存是16,struct3的内存是48,struct1struct2这两个结构体所包含的数据成员是一样的,位置不一样,从而造成内存大小不一样,这就是内存对齐原则造成的。

具体分析

1.结构体struct1
  • a: 8字节,offset为0,即[0 ~ 7]存放a;
  • b: 1字节,offset为8,即[8]存放b;
  • c: 4字节,offset为9,但要找4的倍数,即[12 ~ 15]存放c;
  • d: 2字节,offset为16,16为2的倍数,即[16 ~ 17]存放d;

根据内部最大成员的整数倍原则,struct1的内存大小为24。

2.结构体struct2
  • a: 8字节,offset为0,即[0 ~ 7]存放a;
  • b: 4字节,offset为8,即[8~11]存放b;
  • c: 1字节,offset为12,即[12]存放c;
  • d: 2字节,offset为13,但要找2的倍数,即[14 ~ 15]存放d;

根据内部最大成员的整数倍原则,struct2的内存大小为16。

3.结构体struct3
  • a: 8字节,offset为0,即[0 ~ 7]存放a;
  • b: 4字节,offset为8,即[8~11]存放b;
  • c: 1字节,offset为12,即[12]存放c;
  • d: 2字节,offset为13,但要找2的倍数,即[14 ~ 15]存放d;
  • e: 4字节, offset为16,16为4的整数倍,即[16 ~ 19]存放e;
  • str: 24字节,offset为20,找24的整数倍,即[24 ~ 47]存放str;

根据内部最大成员的整数倍原则,struct3的内存大小为48,其实按结构体成员要从其内部最大元素大小的整数倍地址开始存储原则,str中最大元素是8,找到str的起始位置也是24。

内存对齐原因

  • 内存是以字节为基本单位,cpu在存取数据时,是以为单位存取,并不是以字节为单位存取。频繁存取未对齐的数据,会极大降低cpu的性能。字节对齐后,会减低cpu的存取次数,这种以空间换时间的做法目的降低cpu的开销。

  • cpu存取是以块为单位,存取未对齐的数据可能开始在上一个内存块,结束在另一个内存块。这样中间可能要经过复杂运算在合并在一起,降低了效率。字节对齐后,提高了cpu的访问速率。

思考

在OC中,我们所定义的类其实也是结构体,那么我们是不是在开发中也要注意声明变量的位置,系统会不会帮我们处理这个问题呢。
这里我们新建一个类LGPerson,他的成员和结构体struct1一样, image.png

LGPerson *person = [LGPerson alloc];
person.a = 8.0;
person.b = 'b';
person.c = 5;
person.d = 2;
NSLog(@"%lu", class_getInstanceSize(LGPerson.class));

通过输出我们发现LGPerson的实际内存大小也是24字节,但已知类中有isa要占8个字节,所以LGPerson的成员所占内存大小是16字节。

image.png

通过上图lldb调试信息可以看出,在类的成员属性存储中,系统是进行过优化的。

总结

内存对齐可以提高cpu的存取效率同时提升安全性,会有部分内存的浪费,但是系统又会根据数据存储情况进行内存优化,尽可能降低内存浪费,这样即保证了性能又减少了浪费。

image.png 图中是malloc_size对内存大小的计算,其中就有一个先左移4位,又右移4位的算法,其实就是16字节对齐算法。