1. 获取内存大小的三种方式
- sizeof
- class_getInstanceSize
- malloc_size
1.1 sizeof
- sizeof 是一个
操作符
,不是函数 - 一般使用时,传入的对象是数据类型,这个在编译时决定
- sizeof 最终得到的结果是该数据类型占用空间的大小
1.2 class_getInstanceSize
- runtime提供的 api,用于获取类的实例对象所占用的内存大小
- 返回具体的字节数,其本质是获取实例对象中成员变量的内存大小
1.3 malloc_size
- 获取系统实际分配的内存大小
1.4 代码验证
可以通过代码进行验证:
#import <objc/runtime.h>
#import <malloc/malloc.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *object = [[NSObject alloc] init];
NSLog(@"%lu",sizeof(object));
NSLog(@"%lu",class_getInstanceSize([object class]));
NSLog(@"%lu",malloc_size((__bridge const void *)(object)));
}
@end
输出结果:
2020-12-23 21:23:17.335020+0800 Test[3610:219003] 8
2020-12-23 21:23:17.335198+0800 Test[3610:219003] 8
2020-12-23 21:23:17.335440+0800 Test[3610:219003] 16
1.5 总结
- sizeof
计算类型占用内存大小,其中可以放基本数据类型、对象、指针
对于对象而言,打印的就是对象 objc 的指针大小,所以是 8
对于指针而言,64 位下是 8 个字节
- class_getInstanceSize
计算对象实际占用的内存大小,这个跟类的属性相关,如果类中没有自定义属性,仅继承于 NSObject,则类的实例对象实际占用的内存大小是 8,可以简单理解为 8 字节对齐
- malloc_size
计算系统为对象实际分配的内存,可以看出实际分配的和实际占用的并不相等
2. 结构体内存对齐
下面,我们首先定义两个结构体,分别计算他们的内存大小,以此来引入今天的整体:内存对齐原理。
//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));
下面是输出结果:

两个结构体只有成员变量的顺序有所区别,但是在输出结果中,他们所占用的内存大小却是不相等的,这就是内存对齐现象。
2.1 内存对齐规则
每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16
来改变这一系数,其中的n就是你要指定的“对齐系数”。在iOS
中,Xcode
默认为#pragma pack(8)
,即8字节
对齐。
一般内存对齐的原则主要有3点,可以回看iOS-底层原理 02:alloc & init 源码学习中的说明。
内存对齐规则可以这么理解:
【一】 数据成员的对齐规则可以理解为min(m, n) 的公式, 其中 m表示当前成员的开始位置, n表示当前成员所需要的位数。如果满足条件 m 整除 n (即 m % n == 0), n 从 m 位置开始存储, 反之继续检查 m+1 能否整除 n, 直到可以整除, 从而就确定了当前成员的开始位置。
【二】数据成员为结构体:当结构体嵌套了结构体时,作为数据成员的结构体的自身长度作为外部结构体的最大成员的内存大小,比如结构体a嵌套结构体b,b中有char、int、double等,则b的自身长度为8
【三】最后结构体的内存大小必须是结构体中最大成员内存大小的整数倍,不足的需要补齐。
2.2 验证对齐规则
下表是各种数据类型在iOS中的占用内存大小,根据对应类型来计算结构体中内存大小:

我们可以通过下图图来说明下为什么两个结构体MyStruct1
& MyStruct2
的内存大小打印不一致的情况,如图所示:

结构体 MyStruct1 内存大小计算过程:
- 变量 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
因此MyStruct1
的需要的内存大小为22
字节,而MyStruct1
中最大变量的字节数为8
,所以MyStruct1
实际的内存大小必须是8 的整数倍
,18向上取整到24
,主要是因为24是8的整数倍
,所以 sizeof(MyStruct1)
的结果是24
。
结构体MyStruct2 内存大小计算:
- 变量b:占8个字节,从0开始,此时min(0,8),即 0-7 存储 b
-变量c:占4个字节,从8开始,此时min(8,4),8可以整除4,即 8-11 存储 c
-变量d:占2个字节,从12开始,此时min(12, 2),12可以整除2,即12-13 存储 d
-变量a:占1个字节,从14开始,此时min(14,1),即 14 存储 a
因此MyStruct2
的需要的内存大小为 15
字节,而MyStruct1
中最大变量的字节数为8
,所以MyStruct2
实际的内存大小必须是8 的整数倍
,15向上取整到16
,主要是因为16是8的整数倍
,所以 sizeof(MyStruct2)
的结果是 16
。
网友评论