手撕iOS底层5 -- malloc分析

1,391 阅读3分钟

在上篇着重分析了alloc的流程以及第一个核心点cls->instanceSize 计算需要开辟的内存大小, 通过流程 知道_class_createInstanceFromZone这个方法里的三个核心点


源码三个核心代码


现在分析下第二个关键代码objc = (id)calloc(1, size);这个源码在libmalloc,源码地址下载

NSObject *d = [NSObject alloc];
NSLog(@"%@ - %lu - %lu - %lu",d,sizeof(d),class_getInstanceSize([d class]),malloc_size((__bridge const void *)(d))); // 输出8 - 8 - 16 
  • 第一个输出的8是它的指针大小;

  • 第二个是基于属性的8字节内存对齐(在上篇文章分析过,这里不赘述

  • 第三个的输出48字节, 我们可以通过苹果官方的libmalloc-283.100.6工程来分析下上篇文章里第二个核心点 obj = (**id**)calloc(1, size);做了那些事情;


打开libmalloc,开始接着分析malloc是怎么分配内存的?⛽️

malloc

上图是objc-781源码,通过对上边的调用嫁接到下边的另一份代码里.

malloc源码

  1. 来到libmalloc源码, 继续调试探究; 跳到calloc代码里.

malloc-2

  1. 接着进入到malloc_zone_calloc
void *
malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
	MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);

	void *ptr;
	if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
		internal_check();
	}

	ptr = zone->calloc(zone, num_items, size);
	
	if (malloc_logger) {
		malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE | MALLOC_LOG_TYPE_CLEARED, (uintptr_t)zone,
				(uintptr_t)(num_items * size), 0, (uintptr_t)ptr, 0);
	}

	MALLOC_TRACE(TRACE_calloc | DBG_FUNC_END, (uintptr_t)zone, num_items, size, (uintptr_t)ptr);
	return ptr;
}
  1. 然后开始跳到zone->calloc(zone,num_items,size),发现这里直接到不了方法实现;

点击ptr = zone->calloc(zone, num_items, size);只能看到如下的函数声明;

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 */

这个函数返回一个函数指针,相关知识点可以看文章底部 指针函数和函数指针的内容,所以通过控制台打印出它的指针内容;

点击ptr = zone->calloc(zone, num_items, size);只能看到如下的函数声明; 通过LLDB调试找到zone->calloc(zone, num_items, size); 真正实现default_zone_calloc


  1. 通过搜索来到default_zone_calloc

断点走到return zone->clloc(zone, num_items, size); 让zone = runtime_default_zone执行完,(如果不执行这句,zone是没有值的,在LLDB调试找不到真正实现函数, 会无限递归).

和上一步也是一样的,通过LLDB调试找到真正实现的函数nano_calloc

  1. 跳到nano_calloc函数里

找到关键代码,即_nano_malloc_check_clear函数,

  1. 跳到_nano_malloc_check_clear函数,对于的知识点可以看底部参考,

折叠非关键代码, 找到主线流程segregated_size_to_fit

  1. 跳到segregated_size_to_fit函数里。

这里主要是对齐算法和上篇文章的位与对齐算法是一个道理;通过左移和右移,将低四位置为0, 进行16字节对齐。

内存对齐算法

到现在为止, 知道的16字节对齐的算法有俩种

  • alloc源码分析中的align16
  • malloc源码分析中segregated_size_to_fit

字节对齐到底采用多少字节对齐?

通过这几篇文章写到有8字节对齐和16字节对齐,系统究竟采用哪种字节对齐?

  • 通过之前对class_getInstanceSize的分析,是8字节对齐,相对于一个对象来说,通过8字节分配的内存可以满足对象的需求。
  • 但是苹果粑粑为了防止各种错误,采取16字节对齐,保证容错的空间,内存中的各个对象彼此不是紧巴巴的挨着,对象之间显得宽松一些,虽然浪费一些空间,但苹果也会有各种优化手段,比如 属性重拍等,并且也利于苹果以后的扩展。

拓展

加盐 Salt 参考 维基百科

简单来说加盐是一种提高散列内容的方式;具体可以看维基百科, 解释的很清楚呢;

malloc高级之美

欢迎大佬留言指正😄,码字不易,觉得好给个赞👍 有任何表达或者理解失误请留言交流;共同进步;