iOS之malloc分析

545 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 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