- 在上一篇源码工程的基础上进行下面的分析
malloc的主流程
#import <Foundation/Foundation.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
char *m;
m = (char *)(malloc(24)); //动态分配24个字节
NSLog(@"所占大小%lu",malloc_size(m));
}
return 0;
}
- 在
m = (char *)(malloc(24));
所在代码行打下断点;工程运行在此断点停下;然后Xcode设置Debug -> Debug Workflow -> Always show Disassembly 进入汇编代码界面,然后点击step into按钮进入函数内部,可以看到汇编代码如下:
- 内部执行的是
malloc.c
文件中malloc_zone_malloc
函数,具体底层代码如下:
void * malloc(size_t size)
{
void *retval;
retval = malloc_zone_malloc(default_zone, size);
if (retval == NULL) {
errno = ENOMEM;
}
return retval;
}
- 传入的参数
default_zone
其实是一个“假的”zone,它是malloc_zone_t
类型。它存在的目的就是要引导程序进入一个创建真正的 zone 的流程; - 然后进入
malloc_zone_malloc
函数内部;
void * malloc_zone_malloc(malloc_zone_t *zone, size_t size)
{
MALLOC_TRACE(TRACE_malloc | DBG_FUNC_START, (uintptr_t)zone, size, 0, 0);
void *ptr;
if (malloc_check_start && (malloc_check_counter++ >= malloc_check_start)) {
internal_check();
}
if (size > MALLOC_ABSOLUTE_MAX_SIZE) {
return NULL;
}
ptr = zone->malloc(zone, size); // if lite zone is passed in then we still call the lite methods
if (malloc_logger) {
malloc_logger(MALLOC_LOG_TYPE_ALLOCATE | MALLOC_LOG_TYPE_HAS_ZONE, (uintptr_t)zone, (uintptr_t)size, 0, (uintptr_t)ptr, 0);
}
MALLOC_TRACE(TRACE_malloc | DBG_FUNC_END, (uintptr_t)zone, size, (uintptr_t)ptr, 0);
return ptr;
}
- 其核心代码为
ptr = zone->malloc(zone, size);
其中zone就是default_zone(virtual_default_zone.malloc_zone)是malloc_zone_t
类型; - 当断点执行到
ptr = zone->malloc(zone, size)
时;可以通过下面两个方式知道接下来的执行逻辑:
a> 点击step into按钮进入函数内部,就来到了default_zone_malloc
函数;
b> 在控制台输入LLDB命令p zone->calloc
查找源码实现,控制台输出 如下:
static void * default_zone_malloc(malloc_zone_t *zone, size_t size)
{
zone = runtime_default_zone();
return zone->malloc(zone, size);
}
-
zone = runtime_default_zone();
这里才是真正创建zone;通过runtime_default_zone()
创建真正的zone,然后覆盖传进来的default_zone
;
static inline malloc_zone_t *runtime_default_zone()
{
return (lite_zone) ? lite_zone : inline_malloc_default_zone();
}
- 当断点执行到
return (lite_zone) ? lite_zone : inline_malloc_default_zone();
时,点击step into按钮进入函数内部,就来到了inline_malloc_default_zone()
函数
static inline malloc_zone_t *inline_malloc_default_zone(void)
{
_malloc_initialize_once();
// malloc_report(ASL_LEVEL_INFO, "In inline_malloc_default_zone with %d %d\n", malloc_num_zones, malloc_has_debug_zone);
return malloc_zones[0];
}
- 其内部执行
_malloc_initialize_once()
,继续跟踪
static inline void _malloc_initialize_once(void)
{
os_once(&_malloc_initialize_pred, NULL, _malloc_initialize);
}
- 其内部执行
os_once
函数;参数中传入了接下来要执行的函数指针即_malloc_initialize
;继续执行会来到_malloc_initialize
函数内部,代码较多做了简化只保留核心逻辑如下所示:
static void
_malloc_initialize(void *context __unused)
{
MALLOC_LOCK();
unsigned n;
malloc_zone_t *zone = NULL;
#if CONFIG_NANOZONE
nano_common_configure();
//创建helper_zone
malloc_zone_t *helper_zone = create_scalable_zone(0, malloc_debug_flags);
//创建nanozone_t
zone = nano_create_zone(helper_zone, malloc_debug_flags);
if (zone) {
malloc_zone_register_while_locked(zone);
malloc_zone_register_while_locked(helper_zone);
//helper_zone申请内存空间
malloc_set_zone_name(zone, DEFAULT_MALLOC_ZONE_STRING);
//nanozone_t申请内存空间
malloc_set_zone_name(helper_zone, MALLOC_HELPER_ZONE_STRING);
} else {
zone = helper_zone;
malloc_zone_register_while_locked(zone);
malloc_set_zone_name(zone, DEFAULT_MALLOC_ZONE_STRING);
}
#else
zone = create_scalable_zone(0, malloc_debug_flags);
malloc_zone_register_while_locked(zone);
malloc_set_zone_name(zone, DEFAULT_MALLOC_ZONE_STRING);
#endif
initial_default_zone = zone;
if (n != 0) {
unsigned protect_size = malloc_num_zones_allocated * sizeof(malloc_zone_t *);
malloc_zone_t *hold = malloc_zones[0];
if (hold->zone_name && strcmp(hold->zone_name, DEFAULT_MALLOC_ZONE_STRING) == 0) {
malloc_set_zone_name(hold, NULL);
}
mprotect(malloc_zones, protect_size, PROT_READ | PROT_WRITE);
malloc_zones[0] = malloc_zones[n];
malloc_zones[n] = hold;
mprotect(malloc_zones, protect_size, PROT_READ);
}
......
MALLOC_UNLOCK();
}
-
malloc_zone_t *helper_zone = create_scalable_zone(0, malloc_debug_flags);
是来创建helper_zone的;helper_zone的本质是szone类型;其内部创建下面单独分析; -
nano_create_zone()
函数是创建nanozone_t;其内部创建下面单独分析; -
malloc_set_zone_name(zone, DEFAULT_MALLOC_ZONE_STRING)
为Helper_zone申请内存空间;内部会调用malloc_zone_malloc
;重新再走一遍主流程; -
malloc_set_zone_name(helper_zone, MALLOC_HELPER_ZONE_STRING)
,为nanozone_t申请内存空间;内部会调用malloc_zone_malloc
;重新再走一遍主流程; - 综上所述,这里会创建两种zone分别是szone类型与nanozone_t类型
malloc分支流程 -- nanozone_t的创建
- 上面说到在
_malloc_initialize
函数内部会创建一个nanozone_t类型的zone;
nanozone_t是个结构体,结构如下:
typedef struct nanozone_s {
//first page will be given read-only protection
malloc_zone_t basic_zone;
uint8_t pad[PAGE_MAX_SIZE - sizeof(malloc_zone_t)];
struct nano_meta_s meta_data[NANO_MAG_SIZE][NANO_SLOT_SIZE];
_malloc_lock_s band_resupply_lock[NANO_MAG_SIZE];
uintptr_t band_max_mapped_baseaddr[NANO_MAG_SIZE];
size_t core_mapped_size[NANO_MAG_SIZE];
unsigned debug_flags;
uintptr_t cookie;
malloc_zone_t *helper_zone;
} nanozone_t;
- 创建nanozone_t的函数调用如下:
malloc_zone_t *
nano_create_zone(malloc_zone_t *helper_zone, unsigned debug_flags)
{
nanozone_t *nanozone;
int i, j;
/* get memory for the zone. */
nanozone = nano_common_allocate_based_pages(NANOZONE_PAGED_SIZE, 0, 0, VM_MEMORY_MALLOC, 0);
if (!nanozone) {
_malloc_engaged_nano = NANO_NONE;
return NULL;
}
//basic_zone结构的成员赋值
nanozone->basic_zone.version = 10;
nanozone->basic_zone.size = (void *)nano_size;
nanozone->basic_zone.malloc = (debug_flags & MALLOC_DO_SCRIBBLE) ? (void *)nano_malloc_scribble : (void *)nano_malloc;
nanozone->basic_zone.calloc = (void *)nano_calloc;
nanozone->basic_zone.valloc = (void *)nano_valloc;
nanozone->basic_zone.free = (debug_flags & MALLOC_DO_SCRIBBLE) ? (void *)nano_free_scribble : (void *)nano_free;
nanozone->basic_zone.realloc = (void *)nano_realloc;
nanozone->basic_zone.destroy = (void *)nano_destroy;
nanozone->basic_zone.batch_malloc = (void *)nano_batch_malloc;
nanozone->basic_zone.batch_free = (void *)nano_batch_free;
nanozone->basic_zone.introspect = (struct malloc_introspection_t *)&nano_introspect;
nanozone->basic_zone.memalign = (void *)nano_memalign;
nanozone->basic_zone.free_definite_size = (debug_flags & MALLOC_DO_SCRIBBLE) ? (void *)nano_free_definite_size_scribble
: (void *)nano_free_definite_size;
nanozone->basic_zone.pressure_relief = (void *)nano_pressure_relief;
nanozone->basic_zone.claimed_address = (void *)nano_claimed_address;
nanozone->basic_zone.reserved1 = 0;
nanozone->basic_zone.reserved2 = 0;
mprotect(nanozone, sizeof(nanozone->basic_zone), PROT_READ);
if (debug_flags & MALLOC_ADD_GUARD_PAGES) {
malloc_report(ASL_LEVEL_INFO, "nano zone does not support guard pages\n");
debug_flags &= ~MALLOC_ADD_GUARD_PAGES;
}
/* set up the remainder of the nanozone structure */
nanozone->debug_flags = debug_flags;
if (phys_ncpus > sizeof(nanozone->core_mapped_size) /
sizeof(nanozone->core_mapped_size[0])) {
MALLOC_REPORT_FATAL_ERROR(phys_ncpus,
"nanozone abandoned because NCPUS > max magazines.\n");
}
/* Initialize slot queue heads and resupply locks. */
OSQueueHead q0 = OS_ATOMIC_QUEUE_INIT;
for (i = 0; i < nano_common_max_magazines; ++i) {
_malloc_lock_init(&nanozone->band_resupply_lock[I]);
for (j = 0; j < NANO_SLOT_SIZE; ++j) {
nanozone->meta_data[i][j].slot_LIFO = q0;
}
}
//初始化安全令牌
nanozone->cookie = (uintptr_t)malloc_entropy[0] & 0x0000ffffffff0000ULL;
nanozone->helper_zone = helper_zone;
return (malloc_zone_t *)nanozone;
}
- 在创建完nanozone_t之后,会调用
malloc_set_zone_name()
函数
void
malloc_set_zone_name(malloc_zone_t *z, const char *name)
{
char *newName;
mprotect(z, sizeof(malloc_zone_t), PROT_READ | PROT_WRITE);
if (z->zone_name) {
free((char *)z->zone_name);
z->zone_name = NULL;
}
if (name) {
size_t buflen = strlen(name) + 1;
newName = malloc_zone_malloc(z, buflen);
if (newName) {
strlcpy(newName, name, buflen);
z->zone_name = (const char *)newName;
} else {
z->zone_name = NULL;
}
}
mprotect(z, sizeof(malloc_zone_t), PROT_READ);
}
- 核心
malloc_zone_malloc(z, buflen)
再次进入主流程的调用;在malloc_zone_malloc()函数内部来到
ptr = zone->malloc(zone, size);断点停在这一行,然后使用LLDB命令
p zone->calloc`查找源码实现,控制台输出 如下:
- 可以看到接下来会执行
nano_malloc.c
文件的833行,调用nano_malloc()
函数;如下所示:
static void * nano_malloc(nanozone_t *nanozone, size_t size)
{
if (size <= NANO_MAX_SIZE) {
void *p = _nano_malloc_check_clear(nanozone, size, 0);
if (p) {
return p;
} else {
/* FALLTHROUGH to helper zone */
}
}
malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
return zone->malloc(zone, size);
}
- 从这里可以看出nanozone_t被限制不超过NANO_MAX_SIZE=256;
- size满足条件进入
_nano_malloc_check_clear
函数;其简化后的代码如下:
static void * _nano_malloc_check_clear(nanozone_t *nanozone, size_t size, boolean_t cleared_requested)
{
MALLOC_TRACE(TRACE_nano_malloc, (uintptr_t)nanozone, size, cleared_requested, 0);
void *ptr;
size_t slot_key;
size_t slot_bytes = segregated_size_to_fit(nanozone, size, &slot_key); // Note slot_key is set here
mag_index_t mag_index = nano_mag_index(nanozone);
nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
ptr = OSAtomicDequeue(&(pMeta->slot_LIFO), offsetof(struct chained_block_s, next));
if (ptr) {
} else {
ptr = segregated_next_block(nanozone, pMeta, slot_bytes, mag_index);
}
if (cleared_requested && ptr) {
memset(ptr, 0, slot_bytes);
}
return ptr;
}
-
segregated_size_to_fit()
这个函数内部进行了16字节对齐,表明malloc内存分配最终使用的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; //16
}
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM;
slot_bytes = k << SHIFT_NANO_QUANTUM;
*pKey = k - 1;
return slot_bytes;
}
- NANO_REGIME_QUANTA_SIZE = 16; SHIFT_NANO_QUANTUM = 4;
- 位运算的算法:(size + 16 - 1) >> 4 << 4 实现了16字节对齐;
总结:
- OC对象在分配内存空间时,采用的是
16字节对齐算法
;