什么是内存对齐
计算机中内存空间是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是:在访问特定类型变量
的时候通常在特定的内存地址
访问,这就需要对这些数据在内存中存放的位置有限制,各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
内存对齐是编译器的管辖范围。表现为:编译器为程序中的每个“数据单元”安排在适当的位置上。
内存对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n)
,n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。在ios中,Xcode默认为#pragma pack(8)
,即8字节对齐
- 数据成员的对齐规则:
m
表示当前成员的开始位置
,n
表示当前成员所需要的位数
。如果m 整除 n
, 当前成员从m
位置开始存储n
位, 反之继续检查m+1
能否整除 n
, 直到可以整除, 当前成员从m+1
位置开始存储n
位。
9 10 11 12 - 数据成员为结构体:如果⼀个结构体⾥包含其他结构体成员,则这个结构体成员要从其
内部最⼤元素⼤⼩的整数倍
地址开始存储.(struct a⾥存有struct b,b⾥有char,int ,double等元素,那b应该从8的整数倍开始存储.) - 结构体的
⼤⼩
,必须是其内部最⼤成员的整数倍
.不⾜的要补⻬。
各类数据在iOS中占用的内存大小
数据内存大小表验证内存对齐规则
首先我们定义两个结构体来分析对应的内存大小
struct YPStruct1 {
double a; // 8
char b; // 1
int c; // 4
short d; // 2
}YPStruct1;
struct YPStruct2 {
double a; //8
int b; //4
char c; //1
short d; //2
}YPStruct2;
struct YPStruct3 {
double a; //8
int b; //4
char c; //1
short d; //2
int e; //4
struct YPStruct2 struct2; //16
}YPStruct3;
NSLog(@"YPStruct1 == %lu , YPStruct2 == %lu , YPStruct3 == %lu , YPStruct3.struct2 == %lu",
sizeof(YPStruct1),sizeof(YPStruct2),sizeof(YPStruct3),sizeof(YPStruct3.struct2));
内存打印结果
内存打印结果
YPStruct1内存计算
YPStruct1内存计算-
变量a
:从第0位开始,占8字节 所以存储在[0-7] -
变量b
:从第8位开始,占1字节 所以存储在[8-8] -
变量c
:从第12位开始,占4字节 因为9不能整除4,所以初始位向后移,所以存储在[12-15] -
变量d
:从第16位开始,占2字节 所以存储在[16-17]
因此YPStruct1
的需要的内存大小为17
字节,而YPStruct1中最大变量
的字节数为8
,所以 YPStruct1
实际的内存大小必须是 8 的整数倍
,17向上取整到24,主要是因为24是8的整数倍,所以sizeof(YPStruct1) 的结果是 24
YPStruct2内存计算
YPStruct2内存计算-
变量a
:从第0位开始,占8字节 所以存储在[0-7] -
变量b
:从第8位开始,占4字节 所以存储在[8-11] -
变量c
:从第12位开始,占1字节 所以存储在[12-12] -
变量d
:从第13位开始,占2字节 所以存储在[13-15]
因此YPStruct2
的需要的内存大小为15
字节,而YPStruct2中最大变量
的字节数为8
,所以 YPStruct2
实际的内存大小必须是 8 的整数倍
,15向上取整到16,主要是因为24是8的整数倍,所以sizeof(YPStruct2) 的结果是 16
YPStruct3内存计算
YPStruct3内存计算-
变量a
:从第0位开始,占8字节 所以存储在[0-7] -
变量b
:从第8位开始,占4字节 所以存储在[8-11] -
变量c
:从第12位开始,占1字节 所以存储在[12-12] -
变量d
:从第13位开始,占2字节 所以存储在[13-14] -
变量e
:从第16位开始,占4字节 所以存储在[16-19],因为15不能整除4,所以初始位向后移 -
结构体struct2
:因为struct2
是一个结构体,结构体成员要从其内部最大成员大小的整数倍开始存储
,struct2
的最大成员字节为8
,由于当前是从19开始的,不是8的整数倍,所以要向后移动到24,从24位开始,占16字节 所以存储在[24-39],因为39不是8的整数倍,所以向后移动到40,所以sizeof(YPStruct3) 的结果是 40
内存优化(属性重排)
- 自定义一个YPPerson类,声明几个属性
@interface YPPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
// @property (nonatomic, copy) NSString *hobby;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic) char c1;
@property (nonatomic) char c2;
@end
- 在main中创建YPPerson的实例对象,并对其中的几个属性赋值
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
YPPerson *person = [YPPerson alloc];
person.name = @"Cooci";
person.nickName = @"KC";
person.age = 18;
person.c1 = 'a';
person.c2 = 'b';
NSLog(@"%@",person);
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
- 通过断点调试,根据
YPPerson
的对象地址
,查找对应的属性值
image.png-
0x00000001044e4028
地址代表name的值 -
0x00000001044e4048
地址代表nickName的值 - 但是当我们打印
0x0000001200006261
地址时,发现是乱码,是因为苹果对age、c1、c2属性进行了内存重排
,因为age类型占4个字节,c1和c2类型char分别占1个字节,通过4+1+1的方式,按照8字节对齐,不足补齐的方式存储在同一块内存中-
0x00000012
地址代表age的值 -
0x62
地址代表c1的值,ASCII码
的形式 -
0x61
地址代表c2的值,ASCII码
的形式
-
-
0x0000000000000000
地址代表没有赋值的属性
-
总结
- YPStruct1 通过内存字节对齐原则,增加了9个字节,而YPStruct2通过内存字节对齐原则,通过4+2+1的组合,只需要补齐一个字节即可满足字节对齐规则,这里我们可以知道
结构体内存大小与结构体成员内存大小的顺序有关
- 我们在内存中采用了内存对齐的方式,但并不是所有的内存都可以进行浪费的,苹果会自动对属性进行
重排
,以此来优化内存
网友评论