前言
- x/4gx含义解析,x->16进制格式打印,4->打印4个,g->8字节(w:4字节),x->16进制显示变量
- x p
- p/x person 打印对象所在类的内存地址
- instanceSize 成员变量为8 + 8 + 4时,按8字节对齐应该是24,但结果为什么是32呢?
开辟内存示意图
案例1 - 结构体内存对齐
定义三个结构体,前二者拥有的数据类型是一致的,sizeof(struct)是否一样呢?
struct LGStruct1 {
double a;
char b;
int c;
short d;
}struct1;
struct LGStruct2 {
double a;
int b;
char c;
short d;
}struct2;
struct LGStruct3 {
double a;
int b;
char c;
short d;
int e;
struct LGStruct1 str;
}struct3;
执行NSLog(@"%lu-%lu-%lu",sizeof(struct1),sizeof(struct2),sizeof(struct3));结果为24-16-48
why?
首先我们要了解下内存对齐原则
1、数据成员对齐规则:结构体或者联合体的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如数组,结构体等)的整数倍开始(比如int为4字节,则要从4的整数倍地址开始存储)。通俗来讲就是除了第一个位置,后面的每个成员存放的位置起始位置都要存放的那个成员(如果有子成员那就是子成员)的整数倍位置。
2、结构体作为成员:如果一个结构体里面有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。
3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。 具体分析如下
struct LGStruct1 {
double a; //占8个字节,从0开始 0-7
char b; //占1个字节,从8开始,1可以被8整除,8
int c; //占4个字节,从9开始,4不能被9整除,依次向后推直到12可以,12-15
short d; //占2个字节,从16开始,2可以被16整除,16 - 17
}struct1;
struct LGStruct2 {
double a; //占8个字节,从0开始 0-7
int b; //占4个字节,从8开始,4可以被8整除,8-11
char c; //占1个字节,从12开始,1可以被12整除,12
short d; //占2个字节,从13开始,2不能被13整除,依次向后推直到14可以,14-15
}struct2;
struct LGStruct3 {
double a; //占8个字节,从0开始 0-7
int b; //占4个字节,从8开始,4可以被8整除,8-11
char c; //占1个字节,从12开始,1可以被12整除,12
short d; //占2个字节,从13开始,2不能被13整除,依次向后推直到14可以,14-15
int e; //占4个字节,从16开始,4可以被16整除,16-19
struct LGStruct1 str; //占24个字节,从20开始,24不能被20整除,依次向后推24可以,24-47
}struct3;
上图struct1结果为17,按内存对齐原则,最大字节长度8的整数倍得出24
上图struct2结果为15,按内存对齐原则,最大字节长度8的整数倍得出16
上图struct2结果为47,按内存对齐原则,最大字节长度8的整数倍得出48
内存对齐为什么这样规定呢?答案是方便cpu读取,每次按固定长度读效率更高,针对struct1内存见下图
案例2 - malloc内存分析
首先我们建立一个person类,对应建立几个属性,然后执行对sizeof(),class_getInstanceSize(),malloc_size()的打印。
第一个相信大家都熟知了,重点讲下后面几个:
-
sizeof(person)中的person是指针,一个指针占8个字节,所以sizeof(person) == 8
-
class_getInstanceSize 采用8字节对齐,类实例的字节大小,各属性所占内存大小如下图8+8+4+8=28,加上isa指针的8字节后为36, 按内存对齐原则,总大小须是最长属性大小整数倍,所以结果为40
-
malloc源码
- malloc位于libMalloc库中
- malloc_size 采用16字节对齐,参照整个对象的内存大小,对象实际分配的内存大小必须是16的整数倍,下面我们看下malloc源码:
// 初始化
void *p = calloc(1, 40);
//点击calloc调用如下源码
void *
calloc(size_t num_items, size_t size)
{
return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}
点击_malloc_zone_calloc
这时候点击calloc,只有声明,无法看到函数调用,我们可以po下zone->calloc,找到实现部分
再次po zone-calloc
- 点击_nano_malloc_check_clear,返回值为NULL不用处理,出现help也不用管
这里代表开辟内存大小,点进去查看
- ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), LIFO代表了后进先出,所以ptr在栈区,开篇的堆内存地址赋值给ptr
- segregated_next_block,底层是do while循环,在内存中查找可开篇的地址
这里数据进行右移4位后再左移4位,代表按16字节对齐,所以上面malloc_size(person)为48
总结
- 堆开辟的内存,对象的内存是以16字节对齐
- 开篇我们所说的问题,8 + 8 + 4时,按8字节对齐应该是24,但sizeOf计算堆内存,按16字节对齐,所以为32
- 对象的成员变量 8字节对齐 与成员变量有关,与方法无关
- 对象 对象 16字节
- 对象按8字节读取时,访问出错的概率更高