最近因为工作需要,对iOS底层堆内存分配器libMalloc的开源版本(libmalloc-409.40.6)进行了学习。作为一个使用十分广泛的内存分配器,基于开源代码,研究相关机制,对于iOS内存管理的理解,具有较大价值。和其他内存管理算法类似,libMalloc也需要考虑性能,例如:
-
高效复用,减少内存浪费
-
降低内存分配与回收的时间复杂度
通过阅读分析代码,我们可以学习借鉴到一些其中的设计思路。
关于libMalloc开源代码的分析也有很多资料,本文借鉴了部分文章的分析内容,主要还是基于代码的理解,有理解错误的地方欢迎交流指出。
zone
zone是libMalloc库管理内存的基础概念,malloc提供的最常用的内存操作API,例如malloc、free、realloc等,都是通过zone来实现的。例如,在malloc.c文件中找到malloc函数的实现
void *malloc(size_t size)
{
return _malloc_zone_malloc(default_zone, size, MZ_POSIX);
}
内部调用_malloc_zone_malloc函数,并且传递了3个参数,其中size是业务传递进来的内存分配大小。另外2个参数的含义如下:
static void *_malloc_zone_malloc(malloc_zone_t *zone, size_t size, malloc_zone_options_t mzo)
{
//...
return zone->malloc(zone, size);
}
第3个参数是malloc_zone_options_t类型mzo,在malloc_set_errno_fast函数中使用,用于标记错误码的逻辑。
__options_decl(malloc_zone_options_t, unsigned, {
MZ_NONE = 0x0,
MZ_POSIX = 0x1,
MZ_C11 = 0x2,
});
static MALLOC_INLINE void malloc_set_errno_fast(malloc_zone_options_t mzo, int err)
{
if (mzo & MZ_POSIX) {
#if TARGET_OS_SIMULATOR
errno = err;
#else
(*_pthread_errno_address_direct()) = err;
#endif
}
}
其中err是错误码的值,标记内存分配失败的原因。例如:
#define ENOMEM 12 /* Cannot allocate memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
第一个参数是malloc_zone_t的结构体指针zone,通过zone->malloc实现malloc分配。首先分析zone的数据结构定义malloc_zone_t。
基础结构
malloc_zone_t定义如下:
typedef struct _malloc_zone_t {
void *reserved1; /* RESERVED FOR CFAllocator DO NOT USE */
void *reserved2; /* RESERVED FOR CFAllocator DO NOT USE */
size_t (* MALLOC_ZONE_FN_PTR(size))(struct _malloc_zone_t *zone, const void *ptr); /* returns the size of a block or 0 if not in this zone; must be fast, especially for negative answers */
void *(* MALLOC_ZONE_FN_PTR(malloc))(struct _malloc_zone_t *zone, size_t 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 */
void *(* MALLOC_ZONE_FN_PTR(valloc))(struct _malloc_zone_t *zone, size_t size); /* same as malloc, but block returned is set to zero and is guaranteed to be page aligned */
void (* MALLOC_ZONE_FN_PTR(free))(struct _malloc_zone_t *zone, void *ptr);
void *(* MALLOC_ZONE_FN_PTR(realloc))(struct _malloc_zone_t *zone, void *ptr, size_t size);
void (* MALLOC_ZONE_FN_PTR(destroy))(struct _malloc_zone_t *zone); /* zone is destroyed and all memory reclaimed */
const char *zone_name;
/* Optional batch callbacks; these may be NULL */
unsigned (* MALLOC_ZONE_FN_PTR(batch_malloc))(struct _malloc_zone_t *zone, size_t size, void **results, unsigned num_requested); /* given a size, returns pointers capable of holding that size; returns the number of pointers allocated (maybe 0 or less than num_requested) */
void (* MALLOC_ZONE_FN_PTR(batch_free))(struct _malloc_zone_t *zone, void **to_be_freed, unsigned num_to_be_freed); /* frees all the pointers in to_be_freed; note that to_be_freed may be overwritten during the process */
struct malloc_introspection_t * MALLOC_INTROSPECT_TBL_PTR(introspect);
unsigned version;
/* aligned memory allocation. The callback may be NULL. Present in version >= 5. */
void *(* MALLOC_ZONE_FN_PTR(memalign))(struct _malloc_zone_t *zone, size_t alignment, size_t size);
/* free a pointer known to be in zone and known to have the given size. The callback may be NULL. Present in version >= 6.*/
void (* MALLOC_ZONE_FN_PTR(free_definite_size))(struct _malloc_zone_t *zone, void *ptr, size_t size);
/* Empty out caches in the face of memory pressure. The callback may be NULL. Present in version >= 8. */
size_t (* MALLOC_ZONE_FN_PTR(pressure_relief))(struct _malloc_zone_t *zone, size_t goal);
boolean_t (* MALLOC_ZONE_FN_PTR(claimed_address))(struct _malloc_zone_t *zone, void *ptr);
} malloc_zone_t;
-
reserved1、reserved2:保留字段。
-
函数指针字段:可以理解为内存相关操作的入口函数地址,malloc库对外提供的API,内部会调用zone的对应函数实现,例如size、malloc、calloc、valloc、free、realloc、destroy等。
-
zone_name:zone的名称。
-
version:zone的版本,控制部分逻辑层实现。
-
introspect:malloc_introspection_t结构体指针,处理zone的内部逻辑,例如地址校验、log、统计、锁同步,如下:
default_zone
_malloc_zone_malloc接收了default_zone作为zone,default_zone的初始化代码如下:
typedef struct {
malloc_zone_t malloc_zone;
uint8_t pad[PAGE_MAX_SIZE - sizeof(malloc_zone_t)];
} virtual_default_zone_t;
static virtual_default_zone_t virtual_default_zone
__attribute__((section("__DATA,__v_zone")))
__attribute__((aligned(PAGE_MAX_SIZE))) = {
NULL,
NULL,
default_zone_size,
default_zone_malloc,
default_zone_calloc,
default_zone_valloc,
default_zone_free,
default_zone_realloc,
default_zone_destroy,
DEFAULT_MALLOC_ZONE_STRING,
default_zone_batch_malloc,
default_zone_batch_free,
&default_zone_introspect,
10,
default_zone_memalign,
default_zone_free_definite_size,
default_zone_pressure_relief,
default_zone_malloc_claimed_address,
};
static malloc_zone_t *default_zone = &virtual_default_zone.malloc_zone;
default_zone是virtual_default_zone_t结构体virtual_default_zone的malloc_zone字段,该结构体包含malloc_zone_t数据malloc_zone和uint8_t的pad数组两部分。virtual_default_zone是静态变量,位于__DATA段的__v_zone section中。default_zone的各个函数用于malloc API的具体实现,例如:
-
malloc:default_zone_malloc
-
calloc:default_zone_calloc
-
valloc:default_zone_valloc
-
free:default_zone_free
查看default_zone_malloc。
static void *default_zone_malloc(malloc_zone_t *zone, size_t size)
{
zone = runtime_default_zone();
return zone->malloc(zone, size);
}
MALLOC_ALWAYS_INLINE static inline malloc_zone_t *runtime_default_zone() {
return (lite_zone) ? lite_zone : inline_malloc_default_zone();
}
首先调用runtime_default_zone返回合适的zone,然后使用该zone执行malloc逻辑。runtime_default_zone内部优先使用lite_zone,否则调用inline_malloc_default_zone创建一个zone返回。从这里可以看出,default_zone不负责具体逻辑,而是处理入口逻辑的zone。真正实行逻辑的zone由runtime_default_zone函数返回。
从malloc API到具体的zone,用下图表示:
初始化
inline_malloc_default_zone的实现如下:
MALLOC_ALWAYS_INLINE static inline void _malloc_initialize_once(void)
{
os_once(&_malloc_initialize_pred, NULL, _malloc_initialize);
}
static inline malloc_zone_t *inline_malloc_default_zone(void)
{
_malloc_initialize_once();
return malloc_zones[0];
}
执行初始化zone的操作,代码如下:
static void _malloc_initialize(const char *apple[], const char *bootargs)
{
phys_ncpus = *(uint8_t *)(uintptr_t)_COMM_PAGE_PHYSICAL_CPUS;
logical_ncpus = *(uint8_t *)(uintptr_t)_COMM_PAGE_LOGICAL_CPUS;
//...
set_flags_from_environment();
initial_scalable_zone = create_scalable_zone(0, malloc_debug_flags);
malloc_set_zone_name(initial_scalable_zone, DEFAULT_MALLOC_ZONE_STRING);
malloc_zone_register_while_locked(initial_scalable_zone, /*make_default=*/true);
#if CONFIG_NANOZONE
nano_common_configure();
malloc_zone_t *helper_zone = initial_scalable_zone;
if (_malloc_engaged_nano == NANO_V2) {
initial_nano_zone = nanov2_create_zone(helper_zone, malloc_debug_flags);
}
if (initial_nano_zone) {
malloc_set_zone_name(initial_nano_zone, DEFAULT_MALLOC_ZONE_STRING);
malloc_set_zone_name(helper_zone, MALLOC_HELPER_ZONE_STRING);
malloc_zone_register_while_locked(initial_nano_zone, /*make_default=*/true);
}
#endif
initial_num_zones = malloc_num_zones;
//...
}
整体流程分为:
-
初始化环境变量
-
创建scalablezone,该zone是处理malloc内存的默认zone。
-
如果开启CONFIG_NANOZONE宏,则创建nanozone,V2版本重新实现nanozone的逻辑。
scalablezone
scalablezone是一个较为复杂的zone,针对要处理的内存size,采用tiny、small、medium、large四种不同的策略管理,其数据类型szone_t是malloc_zone_t结构的扩展。
typedef struct szone_s {
malloc_zone_t basic_zone
uint8_t pad[PAGE_MAX_SIZE - sizeof(malloc_zone_t)];
unsigned long cpu_id_key;
unsigned debug_flags;
void *log_address;
struct rack_s tiny_rack;
struct rack_s small_rack;
struct rack_s medium_rack;
_malloc_lock_s large_szone_lock MALLOC_CACHE_ALIGN; // One customer at a time for large
unsigned num_large_objects_in_use;
unsigned num_large_entries;
large_entry_t *large_entries;
size_t num_bytes_in_large_objects;
#if CONFIG_LARGE_CACHE
int large_entry_cache_oldest;
int large_entry_cache_newest;
large_entry_t large_entry_cache[LARGE_ENTRY_CACHE_SIZE_HIGH]; // "death row" for large malloc/free
int large_cache_depth;
size_t large_cache_entry_limit;
boolean_t large_legacy_reset_mprotect;
size_t large_entry_cache_reserve_bytes;
size_t large_entry_cache_reserve_limit;
size_t large_entry_cache_bytes; // total size of death row, bytes
#endif
//...
} szone_t;
主要包含3部分:
-
basic_zone:基础malloc_zone_t类型,定义了内存操作的入口函数地址。
-
rack_s:tiny_rack、small_rack、medium_rack,tiny、small、medium内存策略的数据结构,包含策略所需的字段。
-
large策略相关字段:包括large内存统计信息、管理内存的数据结构,以及cache逻辑相关字段。
相应的,通过create_scalable_zone函数初始化scalablezone。
malloc_zone_t *create_scalable_zone(size_t initial_size, unsigned debug_flags) {
return (malloc_zone_t *) create_scalable_szone(initial_size, debug_flags);
}
szone_t *
create_scalable_szone(size_t initial_size, unsigned debug_flags)
{
szone_t *szone;
szone = mvm_allocate_pages(SZONE_PAGED_SIZE, 0, DISABLE_ASLR, VM_MEMORY_MALLOC);
if (!szone) {
return NULL;
}
//...
unsigned int max_mags = mag_max_magazines();
uint32_t num_magazines = (max_mags > 1) ? MIN(max_mags, TINY_MAX_MAGAZINES) : 1;
rack_init(&szone->tiny_rack, RACK_TYPE_TINY, num_magazines, debug_flags);
rack_init(&szone->small_rack, RACK_TYPE_SMALL, num_magazines, debug_flags);
#if CONFIG_LARGE_CACHE
//...
#endif
szone->cookie = (uintptr_t)malloc_entropy[0];
szone->basic_zone.version = 13;
szone->basic_zone.size = (void *)szone_size;
szone->basic_zone.malloc = (void *)szone_malloc;
szone->basic_zone.calloc = (void *)szone_calloc;
szone->basic_zone.free = (void *)szone_free;
szone->basic_zone.realloc = (void *)szone_realloc;
szone->basic_zone.destroy = (void *)szone_destroy;
szone->basic_zone.batch_malloc = (void *)szone_batch_malloc;
szone->basic_zone.batch_free = (void *)szone_batch_free;
szone->basic_zone.introspect = (struct malloc_introspection_t *)&szone_introspect;
szone->basic_zone.memalign = (void *)szone_memalign;
szone->basic_zone.free_definite_size = (void *)szone_free_definite_size;
szone->basic_zone.pressure_relief = (void *)szone_pressure_relief;
szone->basic_zone.claimed_address = (void *)szone_claimed_address;
szone->basic_zone.try_free_default = (void *)szone_try_free_default;
//...
return szone;
}
-
初始化basic_zone相关字段,定义内存操作的入口函数地址。
-
初始化rack_s数据,初始化tiny、small、medium等策略相关字段。
-
初始化large策略缓存逻辑相关字段。
scalablezone的详细介绍可以参考iOS libMalloc源码分析-ScalableZone。
nanozone
实际场景中,libmalloc参与管理的小内存(256字节)数量巨大且频繁,如果管理不好,将造成很大的内存性能隐患,而scalablezone在策略上更为通用,没有定制优化这部分内存的管理,因此libmalloc提供了nanozone来专门管理这部分内存,源码包含v1和v2版本的实现。这里分析一下V2版本的相关逻辑,初始化相关逻辑:
#if CONFIG_NANOZONE
nano_common_configure();
malloc_zone_t *helper_zone = initial_scalable_zone;
if (_malloc_engaged_nano == NANO_V2) {
initial_nano_zone = nanov2_create_zone(helper_zone, malloc_debug_flags);
}
if (initial_nano_zone) {
malloc_set_zone_name(initial_nano_zone, DEFAULT_MALLOC_ZONE_STRING);
malloc_set_zone_name(helper_zone, MALLOC_HELPER_ZONE_STRING);
malloc_zone_register_while_locked(initial_nano_zone, /*make_default=*/true);
}
#endif
-
nano_common_configure函数设置一些nanozone的全局字段;
-
将scalablezone作为helper_zone,nanov2_create_zone初始化nanozone。
-
设置zone name,注册nanozone为默认zone。
nanozone的数据结构和scalablezone类似,除了basic_zone基础字段,也包含自身逻辑相关字段如下:
typedef struct nanozonev2_s {
malloc_zone_t basic_zone;
uint8_t pad[PAGE_MAX_SIZE - sizeof(malloc_zone_t)];
nanov2_block_meta_t *current_block[NANO_SIZE_CLASSES][MAX_CURRENT_BLOCKS];
_malloc_lock_s current_block_lock[NANO_SIZE_CLASSES][MAX_CURRENT_BLOCKS];
uint16_t delegate_allocations;
unsigned debug_flags;
uint64_t aslr_cookie;
uint64_t aslr_cookie_aligned;
uintptr_t slot_freelist_cookie;
malloc_zone_t *helper_zone;
_malloc_lock_s blocks_lock;
_malloc_lock_s regions_lock;
nanov2_region_t *first_region_base;
os_atomic(nanov2_arena_t *) current_region_next_arena;
_malloc_lock_s madvise_lock;
nanov2_statistics_t statistics;
} nanozonev2_t;
nanov2_create_zone函数创建nanozonev2。
malloc_zone_t *nanov2_create_zone(malloc_zone_t *helper_zone, unsigned debug_flags)
{
nanozonev2_t *nanozone = nano_common_allocate_based_pages(
NANOZONEV2_ZONE_PAGED_SIZE, 0, 0, VM_MEMORY_MALLOC, 0);
if (!nanozone) {
_malloc_engaged_nano = NANO_NONE;
return NULL;
}
nanozone->basic_zone.version = 13;
nanozone->basic_zone.size = OS_RESOLVED_VARIANT_ADDR(nanov2_size);
nanozone->basic_zone.malloc = OS_RESOLVED_VARIANT_ADDR(nanov2_malloc);
nanozone->basic_zone.calloc = OS_RESOLVED_VARIANT_ADDR(nanov2_calloc);
nanozone->basic_zone.valloc = (void *)nanov2_valloc;
nanozone->basic_zone.free = OS_RESOLVED_VARIANT_ADDR(nanov2_free);
nanozone->basic_zone.realloc = OS_RESOLVED_VARIANT_ADDR(nanov2_realloc);
//...
nanozone->helper_zone = helper_zone;
//...
return (malloc_zone_t *)nanozone;
}
这是设置内存管理的入口函数,以及nanozone逻辑的相关字段,详细介绍可以参考iOS libMalloc源码分析-NanoZone。其中scalablezone作为nanozone的helper_zone。
最后,调用malloc_zone_register_while_locked设置nanzone为默认zone。
MALLOC_NOEXPORT void malloc_zone_register_while_locked(malloc_zone_t *zone, bool make_default)
{
//...
if (make_default) {
memmove(&malloc_zones[1], &malloc_zones[0], malloc_num_zones * sizeof(malloc_zone_t *));
malloc_zones[0] = zone;
} else {
malloc_zones[malloc_num_zones] = zone;
}
}
内存策略
初始化操作完成后,接下来分析内存操作中,是如何使用和管理zone的。
分配
malloc
接着分析default_zone_malloc的逻辑,
static void *default_zone_malloc(malloc_zone_t *zone, size_t size)
{
zone = runtime_default_zone();
return zone->malloc(zone, size);
}
如果开启CONFIG_NANOZONE的宏,这里的zone->malloc是nanov2_malloc函数。
#define NANO_MAX_SIZE 256
MALLOC_NOEXPORT void * nanov2_malloc(nanozonev2_t *nanozone, size_t size)
{
size_t rounded_size = _nano_common_good_size(size);
if (rounded_size <= NANO_MAX_SIZE) {
//nanozone分配逻辑
}
return nanozone->helper_zone->malloc(nanozone->helper_zone, size);
}
如果大于256字节或者nano分配失败时,使用scalablezone分配。
calloc
类似的,分析一下calloc的流程:
void *calloc(size_t num_items, size_t size)
{
return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}
static void *_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
malloc_zone_options_t mzo)
{
//...
return zone->calloc(zone, num_items, size);
}
调用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);
}
nanozone的calloc函数nanov2_calloc。
void *nanov2_calloc(nanozonev2_t *nanozone, size_t num_items, size_t size)
{
size_t total_bytes;
if (calloc_get_size(num_items, size, 0, &total_bytes)) {
return NULL;
}
size_t rounded_size = _nano_common_good_size(total_bytes);
if (total_bytes <= NANO_MAX_SIZE) {
//...
}
return nanozone->helper_zone->calloc(nanozone->helper_zone, 1, total_bytes);
}
如果大于256字节或者nano分配失败时,使用scalablezone分配。
回收
回收逻辑较分配逻辑复杂一些,涉及到zone的选择。
void free(void *ptr)
{
if (!ptr) {
return;
}
if (os_unlikely(malloc_instrumented ||
malloc_check_start ||
malloc_logger ||
lite_zone ||
malloc_num_zones == 0 ||
malloc_zones[0]->version < 13 ||
!malloc_zones[0]->try_free_default)) {
find_zone_and_free(ptr, false);
return;
}
malloc_zone_t *zone0 = malloc_zones[0];
zone0->try_free_default(zone0, ptr);
}
如果未实现try_free_default,则find_zone_and_free函数。
void find_zone_and_free(void *ptr, bool known_non_default)
{
malloc_zone_t *zone;
size_t size;
if (!ptr) {
return;
}
zone = find_registered_zone(ptr, &size, known_non_default);
if (!zone) {
//...
} else if (zone->version >= 6 && zone->free_definite_size) {
malloc_zone_free_definite_size(zone, ptr, size);
} else {
malloc_zone_free(zone, ptr);
}
}
-
调用find_registered_zone函数根据要回收的内存地址ptr,选择合适的zone,具体实现如下:
遍历malloc_zones数组,根据ptr查找对应的zone,这里使用zone->size方法来判断,是因为zone分配内存时,内部会存储ptr的size信息,如果zone分配了该内存,则可以通过size方法找到该内存对应的size信息,否则返回0。因此size方法可以作为某个zone是否分配了某块内存的判断方法。
遍历的过程中,判断has_default_zone0逻辑
如果没有注册新zone为malloc_zones[0],返回default_zone。否则返回注册的zone。returned_size存储对应匹配到的size。
-
如果zone实现了free_definite_size,则调用该方法,否则调用free方法。
-
默认返回default_zone的对应方法。
free
如果开启CONFIG_NANOZONE的宏,调用nanozonev2的方法。
MALLOC_NOEXPORT void nanov2_free(nanozonev2_t *nanozone, void *ptr)
{
_nanov2_free(nanozone, ptr, false);
}
static void _nanov2_free(nanozonev2_t *nanozone, void *ptr, bool try)
{
if (ptr) {
nanov2_size_class_t size_class;
nanov2_block_meta_t *block_metap;
size_t size = nanov2_pointer_size_inline(nanozone, ptr, FALSE,
&size_class, &block_metap);
if (size) {
if (malloc_zero_on_free) {
if (size > sizeof(nanov2_free_slot_t)) {
nanov2_bzero((char *)ptr + sizeof(nanov2_free_slot_t),
size - sizeof(nanov2_free_slot_t));
}
}
nanov2_block_meta_t *madvise_block_metap = nanov2_free_to_block_inline(
nanozone, ptr, size_class, block_metap);
if (madvise_block_metap) {
nanov2_madvise_block(nanozone, madvise_block_metap, size_class,
SLOT_CAN_MADVISE);
}
return;
}
}
return try ? nanozone->helper_zone->try_free_default(nanozone->helper_zone, ptr) :
nanozone->helper_zone->free(nanozone->helper_zone, ptr);
}
调用nanov2_pointer_size_inline获取ptr的size,然后执行nanov2_free_to_block_inline回收逻辑,具体参考iOS libMalloc源码分析-NanoZone。如果size信息未获取,则执行helper_zone(scalablezone)的free逻辑。
free_definite_size
逻辑和_nanov2_free类似,执行nanov2_free_to_block_inline回收。
MALLOC_NOEXPORT void nanov2_free_definite_size(nanozonev2_t *nanozone, void *ptr, size_t size)
{
if (ptr && nanov2_has_valid_signature(ptr)) {
nanov2_size_class_t size_class = nanov2_size_class_from_size(size);
if (malloc_zero_on_free) {
if (size_class != 0) {
nanov2_bzero((char *)ptr + sizeof(nanov2_free_slot_t),
size - sizeof(nanov2_free_slot_t));
}
}
nanov2_block_meta_t *madvise_block_metap = nanov2_free_to_block_inline(
nanozone, ptr, size_class, NULL);
if (madvise_block_metap) {
nanov2_madvise_block(nanozone, madvise_block_metap, size_class,
SLOT_CAN_MADVISE);
}
return;
}
return nanozone->helper_zone->free_definite_size(nanozone->helper_zone, ptr,
size);
}
如果size信息未获取,则执行helper_zone(scalablezone)的free_definite_size逻辑。
其他
realloc
realloc更为复杂一些,根据传入的参数决定后续流程
void *realloc(void *in_ptr, size_t new_size)
{
void *retval = NULL;
void *old_ptr;
malloc_zone_t *zone;
old_ptr = (new_size == 0) ? NULL : in_ptr;
if (!old_ptr) {
retval = malloc_zone_malloc(default_zone, new_size);
} else {
zone = find_registered_zone(old_ptr, NULL, false);
if (!zone) {
int flags = MALLOC_REPORT_DEBUG | MALLOC_REPORT_NOLOG;
if (malloc_debug_flags & (MALLOC_ABORT_ON_CORRUPTION | MALLOC_ABORT_ON_ERROR)) {
flags = MALLOC_REPORT_CRASH | MALLOC_REPORT_NOLOG;
}
malloc_report(flags, "*** error for object %p: pointer being realloc'd was not allocated\n", in_ptr);
} else {
retval = malloc_zone_realloc(zone, old_ptr, new_size);
}
}
if (retval == NULL) {
malloc_set_errno_fast(MZ_POSIX, ENOMEM);
} else if (new_size == 0) {
free(in_ptr);
}
return retval;
}
整理逻辑如下:
-
如果in_ptr为NULL或者new_size为0,直接使用defaultzone的malloc逻辑。
-
否则考虑先回收再分配,先查找zone,再执行malloc_zone_realloc逻辑,例如defaultzone的realloc。
void *malloc_zone_realloc(malloc_zone_t *zone, void *ptr, size_t size)
{
//...
new_ptr = zone->realloc(zone, ptr, size);
//...
return new_ptr;
}
- 如果失败且new_size为0,执行free逻辑。
另外,libmalloc还对外提供了一些其他接口,例如:
malloc_size
返回ptr内存的在malloc库中分配的size。
size_t malloc_size(const void *ptr)
{
size_t size = 0;
if (!ptr) {
return size;
}
(void)find_registered_zone(ptr, &size, false);
return size;
}
使用find_registered_zone方法获取size,内部调用zone->size返回,各个zone会实现对应的size方法。
因此,整体流程如图:
自定义zone
这套机制下,libmalloc允许外部注册一个自定义的zone,注册步骤分为2步:
-
自定义zone,设置基本信息,例如zone name,各接口实现函数,例如malloc、realloc、free等。
-
调用malloc_zone_register函数注册新zone,新zone默认加入malloc_zones数组末尾。
相对应,也可以unregister一个zone。
void malloc_zone_unregister(malloc_zone_t *z)
{
for (index = 0; index < malloc_num_zones; ++index) {
if (z != malloc_zones[index]) {
continue;
}
size_t protect_size = malloc_num_zones_allocated * sizeof(malloc_zone_t *);
mprotect(malloc_zones, protect_size, PROT_READ | PROT_WRITE);
malloc_zones[index] = malloc_zones[malloc_num_zones - 1];
--malloc_num_zones;
mprotect(malloc_zones, protect_size, PROT_READ);
initial_num_zones = MAX(MIN(malloc_num_zones, initial_num_zones), 1);
//...
return;
}
}
遍历malloc_zones数组,匹配成功要unregister的zone后,将其替换为malloc_zones数组中末尾的zone,总个数减1。