推荐学习:iOS结构体尺寸 、iOS底层探索02-内存对齐
目录
前言:为什么要进行内存对齐
很多 CPU拒绝读取未对齐数据。当一个程序要求这些 CPU 读取未对齐数据时,这时 CPU 会进入异常处理状态并且通知程序不能继续执行。而且读取未对齐的数据,会大大降低 CPU 的性能。
CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。每次内存存取都会产生一个固定的开销,减少内存存取次数将提升程序的性能。所以 CPU 一般会以 2/4/8/16/32 字节为单位来进行存取操作。我们将上述这些存取单位也就是块大小称为(memory access granularity)内存存取粒度
。
一、如下两个结构体内存大小分别是多少?
struct NAStruct1 {
double a;
char b;
int c;
short d;
} struct1;
struct NAStruct2 {
double a;
int b;
char c;
short d;
} struct2;
二、分析内存占用
-
各种数据类型所占内存大小
WX20200908-101833.png -
结构体内存字节对齐原则
-
数据成员对齐规则:
结构(struct)
(或联合(union)
)的数据成员,第一个数据成员放在offset为0
的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍
开始(比如int为4字节,则要从4的整数倍地址开始存储。 -
结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从
其内部最大元素内存大小的整数倍
地址开始存储。 (struct a里存有struct b,b里有char , int , double等元素,那b应该从8的整数倍开始存储。b里面也需要内存对齐(b的大小是a/b中最大元素内存大小的整数倍)。 ) -
收尾工作:结构体的总大小,也就是sizeof的结果必须是其内部
最大成员
的整数倍,不足的要补齐。
- 对NAStruct1的内存分析如下:
struct NAStruct1 {
double a; // 8 (0 7)
char b; // 1 [8 1] (8)
int c; // 4 [9 4] 9 10 11 (12 13 14 15) 12是4的倍数,因此从地址从12开始
short d; // 2 [16 2] (16 17)
} struct1;
// 内部需要的大小为: 18
// 最大属性 : 8
// 结构体内存大小是最大属性的整数倍: 24
所以NAStruct1、NAStruct2内存分别为:24 、16
三、扩展:结构体嵌套结构体所占内存大小
- NAStruct2的内存大小?
struct NAStruct1 {
double a;
int b;
char c;
short d;
} struct1;
struct NAStruct2 {
double a;
char b;
struct NAStruct1 stru;
int c;
short d;
} struct2;
- 分析
struct NAStruct2 {
double a; //8 (0-7)
char b; //1 (8)
struct NAStruct1 stru; //16 (16 -> 31) 16是NAStruct1 中double (8)的倍数(这里如果排到30 stru也会占用到31位--内部结构体内存对齐)
int c; //4 (32 33 34 35)
short d; //2 (36 37)
} struct2;
// 内部需要的大小为: 38
// 最大属性 : 8
// 结构体内存大小: 40
所以嵌套结构体NAStruct2内存大小为:40
四、OC中类对象的内存分配
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@property (nonatomic, strong) NSString *nickName;
//@property (nonatomic, assign) int sex;
//@property (nonatomic) char ch1;
//@property (nonatomic) char ch2;
@end
NSLog(@"%@ - %lu - %lu - %lu",person,sizeof(person),class_getInstanceSize([Person class]),malloc_size((__bridge const void *)(person)));
打印结果:
<Person: 0x100464680> - 8 - 40 - 48
通过打印发现对象本身大小和系统为对象分配的空间不一致:
- 对象是以
8字节对齐
,内存优化后得到40 - malloc_size开辟的空间是
16字节对齐
,避免对象之间发生溢出和野指针的问题 - 所以对象大小为40时,后面要补8位,最后结果是48
五、内存优化(属性重排)
对象的本质是结构体,为优化内存,编译期间会将OC对象
中的属性从在结构体中从大到小排列。
参考
参考资料
·
网友评论