1. 进入 calloc 函数
int main(int argc, const char * argv[]) {
@autoreleasepool {
int *array = calloc(1, sizeof(int));
array[0] = 10;
printf("array[0] = %d\n",array[0]);
free(array);
array = NULL;
}
return 0;
}
// calloc方法实现
void *
calloc(size_t num_items, size_t size)
{
return _malloc_zone_calloc(default_zone, num_items, size, MZ_POSIX);
}
default_zone 是一个 malloc_zone_t* 结构体指针里边有一个 _malloc_zone_t类型结构体变量 很多跟内存操作的方法都跟 _malloc_zone_t有关.
_malloc_zone_t 是结构体变量 内部有很多分配内存的操作函数, 比如 malloc calloc free size 等函数指针变量
typedef struct _malloc_zone_t {
size_t (* MALLOC_ZONE_FN_PTR(size))(struct _malloc_zone_t *zone, const void *ptr); // 对应default_zone_size函数
void *(* MALLOC_ZONE_FN_PTR(malloc))(struct _malloc_zone_t *zone, size_t size); // 对应default_zone_malloc函数
void *(* MALLOC_ZONE_FN_PTR(calloc))(struct _malloc_zone_t *zone, size_t num_items, size_t size); // 对应default_zone_calloc函数
void *(* MALLOC_ZONE_FN_PTR(valloc))(struct _malloc_zone_t *zone, size_t size); // 对应default_zone_valloc函数
void (* MALLOC_ZONE_FN_PTR(free))(struct _malloc_zone_t *zone, void *ptr); // 对应 default_zone_free函数
void *(* MALLOC_ZONE_FN_PTR(realloc))(struct _malloc_zone_t *zone, void *ptr, size_t size); //对应default_zone_realloc函数
void (* MALLOC_ZONE_FN_PTR(destroy))(struct _malloc_zone_t *zone); //对应default_zone_destroy函数
const char *zone_name; //内存空间的名词
........ 不再一一列举 ..........
} malloc_zone_t;
_malloc_zone_t 内部的函数指针变量指向的就是下边的函数 它们的内存地址是一样的例如 zone->calloc == default_zone_calloc, 意味着真正分配内存的方法 就这下边的函数里
__attribute__((section("__DATA,__v_zone")))
__attribute__((aligned(PAGE_MAX_SIZE))) = {
NULL,
NULL,
default_zone_size, // zone->size(zone) 函数指针调用的就是这个函数
default_zone_malloc, // zone->malloc(zone, size); 函数指针调用的就是这个函数
default_zone_calloc, // zone->calloc(zone, num_items, size); 函数指针调用的就是这个函数
default_zone_valloc, // zone->valloc(default_zone,10); 函数指针调用的就是这个函数
default_zone_free, // zone->free(default_zone,ptr); 函数指针调用的就是这个函数
default_zone_realloc, // zone->realloc() 函数指针调用的就是这个函数
default_zone_destroy, // zone->destroy() 函数指针调用的就是这个函数
...... 不再一一列举其他.......
};
2. 进入_malloc_zone_calloc函数
MALLOC_NOINLINE
static void *
_malloc_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size,
malloc_zone_options_t mzo)
{
MALLOC_TRACE(TRACE_calloc | DBG_FUNC_START, (uintptr_t)zone, num_items, size, 0);
void *ptr;
if (malloc_check_start) {
internal_check();
}
ptr = zone->calloc(zone, num_items, size); // 核心代码分配内存空间并返回指向内存空间的指针地址, zone->calloc函数指针对应的函数实现在 default_zone_calloc中
if (os_unlikely(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);
if (os_unlikely(ptr == NULL)) {
malloc_set_errno_fast(mzo, ENOMEM);
}
return ptr;
}
3. 进入 default_zone_calloc函数
static void *
default_zone_calloc(malloc_zone_t *zone, size_t num_items, size_t size)
{
zone = runtime_default_zone(); // 内部调用inline_malloc_default_zone函数 拿到 malloc_zones[0] 的值
# 内部会调用 nano_calloc函数 因为 在初始化时 nanozone->basic_zone.calloc = (void *)nano_calloc; zone->calloc的值就是nano_calloc函数内存地址
return zone->calloc(zone, num_items, size);
}
4. 进入 nano_calloc 函数
static void *
nano_calloc(nanozone_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;
}
if (total_bytes <= NANO_MAX_SIZE) { // 分配内存小于==256 NANO_MAX_SIZE是一个宏定义默认就是 256 快速分配内存的方式 分配的内存是 16 的倍数要对内存对齐
void *p = _nano_malloc_check_clear(nanozone, total_bytes, 1);
if (p) {
return p;
} else {
/* FALLTHROUGH to helper zone */
}
}
// 大于 256 字节内存 会采用helper_zone.calloc去分配内存 具体函数 szone_malloc_should_clear()
malloc_zone_t *zone = (malloc_zone_t *)(nanozone->helper_zone);
return zone->calloc(zone, 1, total_bytes);
}
5. 进入 _nano_malloc_check_clear 函数
segregated_size_to_fit 计算内存大小
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); // 计算要内存大小 为 16 倍数
mag_index_t mag_index = nano_mag_index(nanozone);
printf("slot_key -> %zu\n",slot_key);
nano_meta_admin_t pMeta = &(nanozone->meta_data[mag_index][slot_key]);
printf("pMeta->slot_bytes -> : %d\n",pMeta->slot_bytes);
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); // TODO: Needs a memory barrier after memset to ensure zeroes land first?
}
return ptr;
}
6. 进入 segregated_size_to_fit函数
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
/*
// 如果 size 传 0 通过 1 << 4 得到 16 1左移四位
1 0000 0000 0000 0001
0000 0000 0001 0000
*/
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior 历史行为 默认最少分配 16 个字节内存大小
}
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // 对量子数进行四舍五入和移位
slot_bytes = k << SHIFT_NANO_QUANTUM; // 乘以两个量子大小的幂
*pKey = k - 1; // Zero-based!
printf("slot_bytes -> %zu\n",slot_bytes);
return slot_bytes;
}
7. 进入segregated_next_block函数
static MALLOC_INLINE void *
segregated_next_block(nanozone_t *nanozone, nano_meta_admin_t pMeta, size_t slot_bytes, unsigned int mag_index)
{
while (1) {
//当前这块pMeta可用内存的结束地址
uintptr_t theLimit = pMeta->slot_limit_addr; // Capture the slot limit that bounds slot_bump_addr right now
//原子的为pMeta->slot_bump_addr添加slot_bytes的长度,偏移到下一个地址
uintptr_t b = OSAtomicAdd64Barrier(slot_bytes, (volatile int64_t *)&(pMeta->slot_bump_addr));
//减去添加的偏移量,获取当前可以获取的地址
b -= slot_bytes; // Atomic op returned addr of *next* free block. Subtract to get addr for *this* allocation.
if (b < theLimit) { // Did we stay within the bound of the present slot allocation?
//如果地址还在范围之内,则返回地址
return (void *)b; // Yep, so the slot_bump_addr this thread incremented is good to go
} else {
//已经用尽了
if (pMeta->slot_exhausted) { // exhausted all the bands availble for this slot?
pMeta->slot_bump_addr = theLimit;
return 0; // We're toast
} else {
// One thread will grow the heap, others will see its been grown and retry allocation
_malloc_lock_lock(&nanozone->band_resupply_lock[mag_index]);
// re-check state now that we've taken the lock
//多线程的缘故,重新检查是否用尽
if (pMeta->slot_exhausted) {
_malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
return 0; // Toast
} else if (b < pMeta->slot_limit_addr) {
//如果小于最大限制地址,当重新申请一个新的band后,重新尝试while
_malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
continue; // ... the slot was successfully grown by first-taker (not us). Now try again.
} else if (segregated_band_grow(nanozone, pMeta, slot_bytes, mag_index)) {
//申请新的band成功,重新尝试while
_malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
continue; // ... the slot has been successfully grown by us. Now try again.
} else {
pMeta->slot_exhausted = TRUE;
pMeta->slot_bump_addr = theLimit;
_malloc_lock_unlock(&nanozone->band_resupply_lock[mag_index]);
return 0;
}
}
}
}
}
至此整个malloc调用流程全部展示完毕, 最主要的过程就是如何分配内存大小, 内存是如何进行16字节对齐的.