-
研究内存对齐原理之前,我们先要熟悉下表:
知道对应的内存大小了,接下来我们需要获取内存,验证是否正确。
验证内存的三种方式:
1、sizeof
2、class_getInstanceSize
3、malloc_size
sizeof
- 1、sizeof是一个操作符,不是函数
- 2、sizeof计算的是对象数据类型的大小,这个大小在编译时确定的而不是运行时
- 3、sizeof最终得到的数据是该数据类型占用空间的大小,当sizeof(结构体)的时候,获取到的是对象指针大小,我们知道一个指针的内存大小是8,所以sizeof获取内存大小时可以放基本数据类型、对象、指针、
calss_getInstanceSize
这个方法是runtime提供的api,用于获取类的实例对象所占用的内存大小,并返回具体的字节数,其本质就是获取实例对象中成员变量的内存大小
malloc_size
这个函数是 获取系统实际分配的内存大小
通过下面的函数打印结果验证上面的说法:
#import <Foundation/Foundation.h>
#import "LGPerson.h"
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *objc = [[NSObject alloc] init];
NSLog(@"objc对象类型占用的内存大小:%lu",sizeof(objc));
NSLog(@"objc对象实际占用的内存大小:%lu",class_getInstanceSize([objc class]));
NSLog(@"objc对象实际分配的内存大小:%lu",malloc_size((__bridge const void*)(objc)));
}
return 0;
}
打印结果如下:
从打印的结果看实际分配的内存和实际内存大小并不相等,不相等的原因是因为内存16字节对齐的原则导致的,内存字节对齐的原理主要有以下三点:
1、数据成员对齐规则
:struct 或者 union 的数据成员,第一个数据成员放在offset=0
的地方,以后每个数据成员存储的其实位置要从该成员大小或者成员子成员大小(只要该成员有子成员比如结构体)的整数倍开始(例如int在32位机中占4字节,则要从4的整数倍地址开始存储)
2、数据成员位结构体
:如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储(例如:struct a里面存有struct b,b里面有char、int、double等元素,则b应该从8的整数倍开始存储)
3、结构体的整体对齐规则
:结构体的总大小,即 sizeof
的结果必须是其内部最大成员的整数倍,不足的要补齐
为什么是16位对齐
1、通常内存是一个个字节组成的,cpu在存取数据时,并不是以字节为单位存储,而是以块为单位存取,块的大小为内存存取力度,频繁存取字节未对齐的数据,会极大降低CPU的性能,所以可以通过减少存取次数来降低cup的开销
2、16字节对齐是因为一个对象中的第一个属性isa
占8字节
,当然对象中肯定还有其他属性,当无属性时会预留8字节,即16字节对齐,如果不预留相当于这个对象的isa
和其它对象的isa
紧挨着,容易造成访问混乱
3、16字节对齐后,可以加快cpu的存取速度,同时增加访问安全性
16字节对齐的算法
- 首先将原始的内存 8 与 size_t(15)相加,得到 8 + 15 = 23
- 将 size_t(15) 即 15进行(取反)操作,(取反)的规则是:1变为0,0变为1
-
最后将 23 与 15的取反结果 进行 &(与)操作,&(与)的规则是:都是1为1,反之为0,最后的结果为 16,即内存的大小是以16的倍数增加的
结构体内存对齐
接下来我们定义两个结构体分别计算他们的大小:
//1、定义两个结构体
struct Mystruct1{
char a; //1字节
double b; //8字节
int c; //4字节
short d; //2字节
}Mystruct1;
struct Mystruct2{
double b; //8字节
int c; //4字节
short d; //2字节
char a; //1字节
}Mystruct2;
//计算 结构体占用的内存大小
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));
输出结果为:
a0000000bbbbbbbbccccdd =24
bbbbbbbbccccdda0=16 //结构体的大小(sizeof)必须是最大成员的整数倍,所以15位需要补一位最后结果是16
两个结构体乍一看,没什么区别,其中定义的变量 和 变量类型都是一致的,唯一的区别只是在于定义变量的顺序不一致,那为什么他们做占用的内存大小不相等呢?其实这就是iOS中的内存字节对齐现象
可以将内存对齐原则可以理解为以下两点:
*【原则一】 数据成员的对齐规则可以理解为min(m, n)
的公式, 其中 m
表示当前成员的开始位置, n
表示当前成员所需要的位数。如果满足条件 m
整除 n
(即m % n == 0
), n
从 m
位置开始存储, 反之继续检查 m+1
能否整除 n
, 直到可以整除, 从而就确定了当前成员的开始位置。
*【原则二】数据成员为结构体:当结构体嵌套了结构体时,作为数据成员的结构体的自身长度作为外部结构体的最大成员的内存大小,比如结构体a
嵌套结构体b
,b
中有char
、int
、double
等,则b
的自身长度为8
*【原则三】最后结构体的内存大小必须是结构体中最大成员内存大小的整数倍,不足的需要补齐。