


进入到malloc_size的实现,发现只有malloc.h文件中对malloc_size的声明找不到实现,但可以发现这个文件属于usr/include/malloc/,说明malloc_size是malloc库里的函数于是就在opensource中下载了 malloc的源码,接者就开启malloc的底层探索吧。

在alloc底层原理的篇章中在objc底层源码的最后定位到了一个特殊的函数在计算出实例变量的大小后,会来到calloc处进行内存的开辟,下一步我们来定位到malloc底层源码中看calloc函数看到底是怎么开辟内存的?







在calloc函数的实现中调用了_malloc_zone_calloc函数,从函数的返回值来看是需要返回一个ptr的指针,可以看到对ptr的初始化在zone->calloc,而点击 查看calloc发现只有一个声明,全局搜索calloc,发现有很多对calloc的赋值,那么有赋值也应该有存储值,有存储值就可以打印值,于是发现zone->calloc其实会调用default_zone_calloc,全局搜索default_zone_calloc,发现这个函数也调用了zone->calloc,继续打印zone->calloc,发现调用了nano_calloc,定位nano_calloc

通过该函数分析是需要返回对象指针,而calloc_get_size函数内返回的是一个NULL,显然不能够,而helper_zone更像是前面的逻辑走失败后的默认处理,通过条件total_bytes <= NANO_MAX_SIZE的判断,是申请的内存应该小于等于这个最大值, 那正常的逻辑就应该是void *p的初始化_nano_malloc_check_clear,运行断点,也的确像我们预测的那样,进入了函数_nano_malloc_check_clear

来到函数内还是先看返回值,返回的ptr,首先TODO不应该是正常逻辑,接着就来到ptr的条件判断,在if里全是一些error的处理,于是会来到segregated_next_block,由于ptr是通过calloc出来的指针地址,即堆区开辟的内存空间,堆区的空间如果不是连续的并混乱,在开辟内存需要在指定的位置创建内存并返回一个地址出去,那么如果当前查询的位置有数据了就得循环查询下一地址,通过源码验证这就是一个循环,而我们实际关心的并不是地址怎么生成的,而是地址的大小,和大小相关的就是 slot_bytes,于是定位到segregated_size_to_fit

通过源码分析这个函数其实执行的是内存对齐的相关操作,即外部传递的(40 + 16 - 1)== 55 换算成二进制00110111 >> 4 << 4 也就是16字节对齐结果为48。所以在系统的内存堆区里面整个对象的内存大小是以16字节对齐。而结构体内部即成员变量之间是以8字节对齐。
为什么要以16字节对齐呢?为不以8字节对齐呢?
虽然成员变量之间8字节对齐是方便了排序读取,但如果对象的内存大小都是8字,那么对象之间的内存是紧挨着的,在内存访问中很容易导致内存访问错误,如果以16字节对齐,那么对象与对象之间的排序就有一定的空余,容错率会更高,16字节对齐从16、32、48、64 内存之间碰到一起的几率是4次,而8字节对齐从8、16、24、32、40、48、56、64内存之间碰到一起的几率是8次,意味着发生错误的概率会增大,另外所有的对象都继承于NSObject,而NSObject默认会有一个isa,也就是说对象最小都会有8字节,而创建对象不添加任何成员变量是很少的,即使写一个int 类型的变量再加上8字节的isa是12字节,再经过8字节对齐就是16字节,也就是说最小都是16字节,这也就是为什在开辟内存时判断小于16时返回16的原因,增加了计算的容度,这也是关于底层内存对齐的算法。