这个函数是获取系统实际分配内存的大小
可以通过以下的代码输出,验证我们上面的说法
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
#import "SATest.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
NSObject *obj = [NSObject alloc];
NSLog(@"%lu",sizeof(obj));
NSLog(@"%zu",class_getInstanceSize([obj class]));
NSLog(@"%zu",malloc_size((__bridge const void*)(obj)));
}
return 0;
}
以下是打印结果
image.png
接下来我们首先定义两个结构体,分别计算他们的内存大小,来引入今天的主体,内存对齐原理
struct MyStruct1{
char a; //1 [0]
double b;//8 [8,9,10,11,12,13,14,15]
int c;//4 [16,17,18,19]
short d;//2 [20,21]
}MyStruct1;
struct MyStruct2{
char a; //[14]
double b; //[0,1,2,3,4,5,6,7]
int c; //[8,9,10,11]
short d; //[12,13]
}MyStruct2;
NSLog(@"结构体1- %lu 结构体2- %lu",sizeof(MyStruct1),sizeof(MyStruct2));
打印结果如下
image.png
从打印结果可以看出一个问题,两个结构体看起来没什么区别,唯一的区别就是其中的变量顺序不一致,导致他们所占用内存大小不相等,这就是ios 中内存字节对齐现象
每个特定平台上的编译器都有自己的默认"对齐系数",程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中n
就是对齐系数,在 ios 中,xcode 默认是#pragma pack(8),即 8 字节对齐
内存对齐原则主要分为以下三点
下表是各种数据类型
image.png
我们可以通过下图来说明为什么两个结构体MyStruct1和 MyStruct2 内存大小为什么不一致的情况
image.png
首先定义一个MyStruct3,其中嵌套MyStruct2.如下所示
//1、结构体嵌套结构体
struct Mystruct3{
double b; //8字节
int c; //4字节
short d; //2字节
char a; //1字节
struct Mystruct2 str;
}Mystruct3;
//2、打印 Mystruct3 的内存大小
NSLog(@"Mystruct3内存大小:%lu", sizeof(Mystruct3));
NSLog(@"Mystruct3中结构体成员内存大小:%lu", sizeof(Mystruct3.str));
打印结果如下
image.png
image.png
再次计算一个结构体,验证内存大小
struct Mystruct4{
int a; //4字节 min(0,4)--- (0,1,2,3)
struct Mystruct5{ //从4开始,存储开始位置必须是最大的整数倍(最大成员为8),min(4,8)不符合 4,5,6,7,8 -- min(8,8)满足,从8开始存储
double b; //8字节 min(8,8) --- (8,9,10,11,12,13,14,15)
short c; //2字节,从16开始,min(16,2) -- (16,17)
}Mystruct5;
}Mystruct4;
分析如下
MyStruct1通过内存字节对齐原则,增加了 9 个字节,而 Mystruct2只增加了一个字节,结构体内存大小与结构体内的成员顺序有关
举个例子说明属性重排,也就是内存优化
@interface YXPerson : NSObject
@property(nonatomic,copy)NSString * name;
@property(nonatomic,copy)NSString * nickName;
@property(nonatomic,assign)int age;
@property(nonatomic)char c1;
@property(nonatomic)char c2;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
YXPerson *person = [[YXPerson alloc] init];
person.name = @"YX";
person.nickName = @"XC";
person.age = 18;
person.c1 = "a";
person.c2 = "b";
NSLog(@"");
}
return NSApplicationMain(argc, argv);
}
image.png
* 当我们想通过地址0x000000120000aba9找出 age,c1,c2 的值时,发现是乱码,这是因为苹果针对 age&c1&c2 的属性内存进行了重排,age 占 4 字节,c1 和 c2 各占一个字节, 所以他们三个存储在同一块内存中age 的获取通过0x00000012c1 的获取通过0x61(a的 ASCII码是 97)c2 的获取通过0x62(b 的ASCII码是98)
image.png
下图是 person 内存分布情况
person内存结构.png
注意undefined char 类型的数据读取出来是以ASCII 码的形式显示
这里总结下苹果的内存对齐思想
前面我们提到了 8 字节对齐,也提到了 16 字节对齐,我们到底是按照哪种进行对齐的呢
我们可以通过 objc 源码中的class_getInstanceSize进行分析
size_t class_getInstanceSize(Class cls)
{
if (!cls) return 0;
return cls->alignedInstanceSize();
}
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
# define WORD_MASK 7UL
综合前文提到的获取内存大小的方式,
目前已知的16 字节内存对齐算法有两种
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。