64位系统各个类型的内存占用情况
我们需要清楚各个基本类型占用情况,值得注意的是:64位 系统的指针是 64位,即 8字节
| 类型 | 内存占用情况(字节Byte) |
|---|---|
| char | 1 |
| int | 4 |
| float | 4 |
| long | 8 |
| double | 8 |
| 指针 | 8 |
内存对齐
- 对象的内存占用并不是其成员变量的简单总和🙅
- 在
iOS中,会发生两次内存对齐,即 最终内存占用 >= 成员变量的总和 🙆 - 第一次内存对齐发生在
instanceSize中,计算成员变量的内存大小,并限制最小内存为16字节
// runtime库代码 instanceSize方法
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
- 第二次内存对齐发生在
calloc中,即申请内存的时候会在iOS的Buckets sized内存桶申请一块指定大小的内存
// malloc库代码 calloc方法
// count:内存个数,默认1
// size:成员变量的内存大小,拿第一次对齐后的数据
calloc(count, size);
// calloc方法会在iOS内存桶内申请内存
// iOS内存桶,会指定大小,规则为16的倍数,上限为256
// 例如24则申请32
Buckets sized {16, 32, 48, 64, 80, 96, 112, ..., 256}
NSObject对象内存计算
- 根据
OC转C++的代码,我们可以看到NSObject的结构体是这样的,包含一个ISA指针 - 成员变量
ISA的占用为8字节,但是NSObject的内存占用却是16字节,是因为发生了两次内存对齐
// NSObject_IMPL内部存放ISA指针
struct NSObject_IMPL {
Class isa; // 8个字节
};
其他对象内存计算
- 根据
OC转C++的代码,我们可以看到People的结构体是这样的,包含一个ISA指针,一个int类型 - 成员变量
ISA的占用为8字节,int类型的占用为4字节但是People的内存占用却是16字节,也是是因为发生了两次内存对齐 - 如果是使用
@property,除了添加成员变量,还会重写get/set方法,get/set方法会存放在类对象方法里面,而不是放在实例对象内部
// OC源码
@interface People : NSObject {
@public
int _count;
}
@end
// 转为C++,添加_IMPL后缀,生成对应结构体
struct People_IMPL {
struct NSObject_IMPL NSObject_IVARS; // 8个字节
int _count; // 4个字节
};
// NSObject_IMPL内部存放ISA指针
struct NSObject_IMPL {
Class isa; // 8个字节
};
附录:查看内存的方法
方法查看内存
#import <objc/runtime.h>
// 方法一
// 获取实例对象的大小,不推荐使用,有误差
// 输入:Class类
// 返回:成员变量所占空间大小,且会发生内存对齐
// 对齐规则:最大成员变量的倍数
// 例子输出结果为8
NSLog(@"%zd",class_getInstanceSize([NSObject class]));
#import <malloc/malloc.h>
// 方法二🌟
// C语言函数,需要__bridge桥接,推荐使用
// 输入:实例对象
// 返回:指针所指内存大小
// 例子输出结果为 16
NSLog(@"%zd",malloc_size((__bridge const void *)([[NSObject alloc] init])));
// 方法三
// 宏定义类似的获取内存方法
// 输入:基本数据类型或者结构体
// 输出:计算类型或表达式在内存中的大小
// 在编译时期计算的,而不是在运行时期
// 它仅用于计算类型或表达式在内存中的大小,并不会对其进行实际的计算或执行
NSLog(@"%zd",sizeof(int));
Xcode查看内存工具
- 访问对象地址,该对象内存为
16字节,每个数字代表16进制的1个字节 - 前八个字节存放
TestClass的ISA指针地址,后八个字节存放int类型属性,明显int类型属性的值为5 iOS内存阅读方式为大端(big-endian),即高地址往低地址读取,当然也有另外一种相反的阅读方式则为小端(little-endian)
LLDB查看内存
p test输出地址po test输出对象x test输出对象内存