开始之前,我们先放上一张不同的数据类型所占用的大小的表,免的后面再计算的时候有些懵逼
数据类型的大小如果后面在计算内存占用的时候不清楚,请翻到这里查看。下面的例子里面都是在64位机器上运行
这里是分割线,下面的内容纯属装逼~~~
例1
我们先定义一个结构体:
struct WStruct {
double a;
int b;
char c;
short d;
}WStruct1;
我们知道了这个结构体每个元素所占用的内存空间如下:
Double | 8 |
---|---|
Int | 4 |
Char | 1 |
Short | 2 |
这样我们计算出WStruct1
结构体所占用的内存应该为15字节
,但是我们使用sizeof打印一下看看结果:
NSLog(@"%lu",sizeof(WStruct1));
001-内存对齐原则[75942:873202] 16
打印出的结果为16,这是为什么呢?
这是因为系统自动做了
内存对齐
为什么要内存对齐,以及系统怎么做的字节对齐,是接下来我们要研究的东西
为什么要内存对齐
要搞清楚这个问题,我们要清楚
- 如果不内存对齐,数据是怎么存储
- 如果不内存对齐,数据怎么读取
我们在看看这样存储以后,数据是怎么读取的:
未内存对齐数据读取
如果不做内存对齐,那么在读取的时候就需要不停的去变换读取的长度,这是非常消耗性能的,会大大降低读取效率
那么既然我们知道了数据在存储的时候会进行内存对齐,那么内存对齐是按照什么规则进行的呢?
内存对齐的规则
1、数据成员对⻬规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。
2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补⻬
以上就是内存对齐的文字描述,接下来我们还是结合上面结构体来验证一下:
struct WStruct {
double a;
int b;
char c;
short d;
}WStruct1;
内存对齐的存储
我们假设从位置0
开始存储:
double
占用8个字节
,所以占用0-7
位置的内存空间
由于0-7
已经被double占用,在存储int
的时候至少要从8号位置开始,我们知道int
占用4个字节,8正好是4的整数倍
所以int占用8-11
的内存空间
接下来存储char
,char占用1个字节
,所以直接放在12
内存地址
short
占用2个字节
的内存,但是开始存储的位置13
并不是2的整数倍,所以我们需要将short的存储位置后移一个字节,从14
开始存储,占用14-15
内存空间
通过这种方式,WStruct1结构体就会占用16个字节的内存空间。
内存对齐的读取
进行内存对齐后,我们在读取这个结构体的时候,会按照结构体中占用最大内存属性的整数倍进行读取,也就是说,WStruct1通过两次读取就可以读取出所有的数据
那么接下来我们通过一个复杂的例子来计算一下所占用的内存空间:
例子2
struct WStruct2 {
double a;
char b;
int c;
short d;
struct WStruct e;
}WStruct2;
我们看到这个例子中包含了一个结构体属性,那么这样的结构体需要占用多少内存空间呢?
WStruct2占用内存空间
上图就是WStruct2
所占用的内存空间,我们看打印结果
001-内存对齐原则[77470:954388] 40
也是40个字节
总结
内存对齐实际上是牺牲了一些内存空间来让内存数据的读取更加快速,是一种空间换时间的过程。我们上一章节说明了再iOS系统中,普遍采用16字节对齐的方式来进行存储,本篇讲的是具体数据类型的存储,16字节对齐主要针对对象的存储,和本篇所讲的内存对齐并不冲突
网友评论