iOS之malloc分析

语言: CN / TW / HK

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

malloc函数是alloc的核心方法之一,负责开辟内存空间

项目中,只能找到malloc_size的方法定义,它的代码实现在libmalloc源码中

image.png

探索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函数

image-2.png 源码中只能找到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

image-3.png - 找到函数的真身:default_zone_calloc 在全局搜索calloc时,虽然找不到函数实现,但是找到了calloc赋值代码。有赋值必然会存储值,通过打印也许可以得到线索

或者,尝试Always Show Disassembly查看汇编代码

image-4.png - 也可得到相同线索: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打印

image-5.png 来到nano_malloc函数

image-6.png 进入_nano_malloc_check_clear函数 image-7.png - 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_SIZESHIFT_NANO_QUANTUM`定义:

```

define SHIFT_NANO_QUANTUM 4

define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16

`` -1左移4位,即:16进入segregated_next_block`函数,开辟内存空间

image-8.png - 堆区开辟的空间是不连续的,期间可能因多线程、小于最大限制地址等原因,需要重新尝试while。当开辟空间成功,返回指针地址

内存对齐算法

segregated_size_to_fit函数中,内存对齐的算法为

(size + 15) >> 4 << 4

算法作用为16字节对齐,保证分配的内存大小,必须是16的整数倍,与算法(x + N) & ~N有异曲同工之妙。\ 假设:传入的size40\ size + 15 = 550011 0111 \ 右移4位0000 0110\ 左移4位0011 0000\ 转换10进制为48

结构体内部,成员变量以8字节对齐。但是在堆区分配对象的内存大小,以16字节对齐

系统为什么要这样设计?

假设,堆区分配对象的内存大小,也按照8字节对齐。读取时,遇到多个连续存储的8字节对象,容易出现野指针或内存越界访问

再有,NSObject自身占8字节,自定义对象一般来说也会有自定义的成员变量,所以自定义对象的大小,在大部分情况下,不会小于16字节

所以,在堆区分配对象的内存大小,16字节对齐为最好的选择

malloc流程图

image-9.png