iOS - 内存对齐分析
iOS 内存对其原则
-
数据成员对⻬规则
:结构(struct)(或联合(union))
的数据成员,第⼀个数据成员
放在offset为0
的地⽅,以后每个数据成员存储的起始位置要从该成员⼤⼩
或者成员的⼦成员⼤⼩(只要该成员有⼦成员,⽐如说是数组,结构体等)的整数倍
开始(⽐如int为4字节,则要从4的整数倍地址开始存储。 -
结构体作为成员
:如果⼀个结构⾥有某些结构体成员,则结构体成员要从其内部最⼤元素
⼤⼩的整数倍地址开始存储.(struct a⾥存有struct b,b⾥有char,int ,double等元素,那b应该从8的整数倍开始存储.) -
收尾⼯作:结构体的总⼤⼩,也就是sizeof的结果,.必须是其
内部最⼤成员的整数倍
,不⾜的要补⻬
。
看这些文字比较难理解接下来例子解析,先了解一下基本数据类型所需要的内存
基本数据类型所占用的内存大小设置一个结构体
struct Mystruct{
char a; //1字节
double b; //8字节
int c; //4字节
short d; //2字节
}Mystruct1;
NSLog(@"%lu-%lu",sizeof(Mystruct1);
根据以上规则来计算结构体的大小,设min(m,n)m为起始位置,n为所需内存
-
变量a
:占1
个字节,从0
开始,此时min(0,1)
,即0
存储a
-
变量b
:占8
个字节,从1
开始,此时min(1,8)
,1不能整除8,继续往后移动,知道min(8,8),从8开始,即8-15
存储b
-
变量c
:占4
个字节,从16
开始,此时min(16,4)
,16可以整除4,即16-19
存储c
-
变量d
:占2
个字节,从20
开始,此时min(20, 2)
,20可以整除2,即20-21
存储d
- 收尾工作:通过计算
Mystruct
需要内存为21
字节,但Mystruct中最大成员需要8
个字节,所以Mystruct所需内存必须为8的整数倍
为24
不足则补齐。
还有一种情况就是结构体
中嵌套结构体1
,则把结构体1
内部最大成员大小
看作结构体大小
,如:
//1、结构体嵌套结构体
struct Mystruct2{
double a; //8字节
struct Mystruct b //内部最大double b 8字节;
}Mystruct2;
NSLog(@"%lu-%lu",sizeof(Mystruct1),sizeof(Mystruct2));
-
变量a
:占8
个字节,从0
开始,此时min(0,8)
,即0-7
存储a
-
变量b
:占24
个字节,从8
开始(该结构体内部最大为double
是8
字节),此时min(8,24)
,即8-31
存储b
- 收尾工作:通过计算
Mystruct2
需要内存为31
字节,但Mystruct2
中最大成员需要8
个字节,所以Mystruct2
所需内存必须为8的整数倍
为32
不足则补齐。 image.png
获取内存大小的三种方式
-
sizeof
计算类型占用的内存
大小,可计算基本数据类型,对象,指针
- 计算基本数据类型占用的内存,每个类型所占用的内存大小不同
- 计算对象类型、指针,该类所占内存大小为8,可以计算验证
sizeof([NSObject alloc] init])
-
class_getInstanceSize
计算对象实际占用内存大小
,该内存大小是依据对象的属性
来计算的,如果对象没有属性则占用的内存为8,该方法用到8字节对齐word_align()
-
malloc_size
计算对象实际分配的内存大小
,这个计算是由系统完成的,实际分配的内存和实际占用内存不一定相等,该方法用到16字节对齐
。
内存优化
对象中有相同的属性,但顺序不同的话他们所占的内存会相同吗?
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;
image.png
很显然,属性的排列顺序
不同会导致对象所占的内存大小
不同。
通过测试可以知道,若是结构体
中的成员是根据内存从大到小
的顺序定义, 那么根据内存对齐规则来计算结构体内存大小时只需要补齐少量的内存填充
即可满足内存对齐规则,这就是苹果采用的利用空间换时间
。
总结
所以,这里可以总结下苹果中的内存对齐思想:
- 大部分的内存都是通过
固定的内存块
进行读取, - 尽管我们在内存中采用了
内存对齐
的方式,但并不是所有的内存都可以进行浪费的,苹果会自动对属性进行重排
,以此来优化内存
。 - 对于一个对象来说,其真正的对齐方式 是 8字节对齐,
8字节
对齐已经足够满足对象的需求了 - apple系统为了
容错
,采用的是16字节
对齐的内存,主要是因为采用8字节对齐时,两个对象的内存会紧挨着
,显得比较紧凑,而16字节比较宽松,利于苹果以后的扩展
。 -
class_getInstanceSize
:是采用8字节对齐
,参照的对象的属性内存大小
-
malloc_size
:采用16字节对齐
,参照的整个对象的内存大小
,对象实际分配的内存大小必须是16的整数倍