malloc是谁?malloc是怎么分配内存的?malloc分配的是什么内存?
最常用且最便捷的内存分配方式当然是栈内存了,随着栈指针的上下移动,完成了压栈出栈操作,编译器替我们规划好了栈上内存的申请、释放。
但是呢,栈内存是有限且可怜的。一方面,是因为没有程序需要那么多的栈内存,另一方面,栈内存的软限制防止了无尽的递归操作耗尽系统内存的可能。如下,可查看一般栈内存的大小也只有8MB.
[root@localhost ~]# ulimit -s
8192
glibc是GNU发布的libc库,即GNU标准的C函数库。malloc是其中提供的一个函数接口,为应用程序提供通用的内存分配接口。
我们都听过,局部变量在栈上分配内存,并由高地址向低地址增长,动态内存在堆上分配,并由低地址向高地址增长。具体细节是怎么样的,还是要看下源码如何做的。
在malloc.c中可以看到malloc是__libc_malloc的别名:
strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)
__libc_malloc定义如下:
void * __libc_malloc (size_t bytes)
{
mstate ar_ptr;
void *victim;
void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0));
...
arena_get (ar_ptr, bytes);
victim = _int_malloc (ar_ptr, bytes);
...
return victim;
}
初次进来会先调用__malloc_hook,初始化分配区main_arena,这里维护了申请内存的各种信息:
struct malloc_state
{
/* Serialize access. */
__libc_lock_define (, mutex);
/* Flags (formerly in max_fast). */
int flags;
/* Set if the fastbin chunks contain recently inserted free blocks. */
/* Note this is a bool but not all targets support atomics on booleans. */
int have_fastchunks;
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;
/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;
/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2];
/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];
/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;
/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
内存块由chunk维护,并被组织成链表:
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
然后再调用__libc_malloc的一个封装:
static void * malloc_hook_ini (size_t sz, const void *caller)
{
__malloc_hook = NULL;
ptmalloc_init ();
return __libc_malloc (sz);
}
初始化完成后,将进入核心分配函数_int_malloc:进来先调用checked_request2size将申请的size转换为size + sizeof(chunk)的大小,并对齐到16字节的整数倍:
static inline bool checked_request2size (size_t req, size_t *sz) __nonnull (1)
{
if (__glibc_unlikely (req > PTRDIFF_MAX))
return false;
*sz = request2size (req);
return true;
}
首先,尝试在fast_bin中查找对应的空闲块,如果申请的块size小于64B,则有可能直接找到返回:
if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ())) {
idx = fastbin_index (nb);
mfastbinptr *fb = &fastbin (av, idx);
mchunkptr pp;
victim = *fb;
if (victim != NULL) {
...
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
}
如果没找到,再判断申请的size小于512B,尝试从smallbin中查找对应的空闲块,否则会尝试合并fastbin中的碎片:
if (in_smallbin_range (nb)) {
idx = smallbin_index (nb);
bin = bin_at (av, idx);
if ((victim = last (bin)) != bin) {
...
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
} else {
idx = largebin_index (nb);
if (atomic_load_relaxed (&av->have_fastchunks))
malloc_consolidate (av);
}
如果还是没有,会做一些垃圾回收的操作:处理最近释放的块,仅在size完全匹配的时候才返回这个块,否则就把块按大小放入对应的bin中。
对于仍未找到空闲块的情形,将会调用sysmalloc向内核分配一块新的空间:
void *p = sysmalloc (nb, av);
if (p != NULL)
alloc_perturb (p, bytes);
sysmalloc先判断size是否大于128KB,若是,则通过mmap申请一块内存,将其映射到堆栈段中间的内存映射段,否则通过向上调整brk指针的方式增大虚拟内存边界,然后返回。
malloc无论是通过mmap系统调用,或是调整brk指针申请内存,申请的都还只是虚拟内存,并没有分配实际的物理页。应用程序看到malloc返回的地址,就已经认为内存申请成功了。
至于物理内存页,要等到应用程序初次使用这部分内存块时,才会真正的通过内核来分配。