一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
malloc函数是alloc的核心方法之一,负责开辟内存空间
项目中,只能找到malloc_size的方法定义,它的代码实现在libmalloc源码中
探索libmalloc源码
进入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的函数声明,但是无法进入
void *(* MALLOC_ZONE_FN_PTR(calloc))(struct _malloc_zone_t *zone, size_t num_items, size_t size); /* same as malloc, but block returned is set to zero */
在项目中,搜索calloc关键字,没有找到任何线索
这种情况,可以尝试打印zone->calloc
- 找到函数的真身:
default_zone_calloc在全局搜索calloc时,虽然找不到函数实现,但是找到了calloc赋值代码。有赋值必然会存储值,通过打印也许可以得到线索
或者,尝试Always Show Disassembly查看汇编代码
- 也可得到相同线索:
default_zone_calloc来到default_zone_calloc函数
static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size) {
zone = runtime_default_zone();
return zone->calloc(zone, num_items, size);
}
又遇到了zone->calloc函数,继续使用lldb打印
来到
nano_malloc函数
进入
_nano_malloc_check_clear函数
segregated_size_to_fit:计算内存大小segregated_next_block:开辟内存空间 进入segregated_size_to_fit函数,计算出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;
}
NANO_REGIME_QUANTA_SIZE和SHIFT_NANO_QUANTUM定义:
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16
1左移4位,即:16进入segregated_next_block函数,开辟内存空间
- 堆区开辟的空间是不连续的,期间可能因多线程、小于最大限制地址等原因,需要重新尝试
while。当开辟空间成功,返回指针地址
内存对齐算法
在segregated_size_to_fit函数中,内存对齐的算法为
(size + 15) >> 4 << 4
算法作用为
16字节对齐,保证分配的内存大小,必须是16的整数倍,与算法(x + N) & ~N有异曲同工之妙。
假设:传入的size为40
size + 15 = 55,0011 0111
右移4位,0000 0110
左移4位,0011 0000
转换10进制为48
结构体内部,成员变量以8字节对齐。但是在堆区分配对象的内存大小,以16字节对齐
系统为什么要这样设计?
假设,堆区分配对象的内存大小,也按照8字节对齐。读取时,遇到多个连续存储的8字节对象,容易出现野指针或内存越界访问
再有,NSObject自身占8字节,自定义对象一般来说也会有自定义的成员变量,所以自定义对象的大小,在大部分情况下,不会小于16字节
所以,在堆区分配对象的内存大小,16字节对齐为最好的选择
malloc流程图