1. GLIBC内存管理核心结构
1.1. malloc_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;
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
struct malloc_chunk,该结构是GLIBC中内存管理的最小单元,每个用户分配的最小内存块必须为“2 * (user_size + offsetof(struct malloc_chunk, fd_nextsize))”,且与sizeof(size_t)的大小进行对齐。在32位机上size_t为4个字节,64位机上为8个字节。
1.2. malloc_state
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;
};
malloc_state结构是GLIBC进行内存分箱管理的核心基础结构。
在多线程环境中,为了提高内存管理效率,GLIBC对其提供了不同的管理策略,主要有:主线程管理和线程管理,即main_arena(该结构是一个全局静态变量,存放在内存数据段)和thread_arena(主要为线程提供内存管理机制)。这样GLIBC就会以main_arena为核心,将所有内存管理内存管理arena与main_arena进行管理起来(用一个双向链表进行管理)。
注:arena是malloc_state结构
1.3. 内存管理arena是否是无限增大?
答案是否定的,arena的大小将会受到系统CPU核数的限制,一般为:核数 * sizeof(size_t)。
1.4. arena内存管理原理
对进程内的每个arena必须保证其原子操作,这样就会对每个arena在进行内存分配和释放时进行加锁保护。在多线程环境中,内存分配策略为:当用户请求一块内存时,GLIBC首先会从main_arena开始,先获取该arena的锁,如果成功则基于该arena进行内存分配,否则继续向下个arena进行查找。如果所有arena查找完毕均不能满足用户请求的内存大小,则GLIBC将会利用chunk_top(下文介绍)来为用户分配内存。
2. arena内存分箱管理
从malloc_state结构中可知,该结构包含fastbinsY,top,bins和binmap等核心结构。
2.1. fastbinsY
fastbinsY字面意思就是快速箱子,在内存管理过程中除了tcache外,它的优先级最高。工作原理:在用户请求内存时,arena将会从其对应的fastbinsY中进行查找,看如果满足用户请求,则直接将其返回给用户使用。在fastbinsY中,其内存大小一般为128bytes(64位机中)。
2.2. bins
bins是一个大小为128的大数组,并将其分为4类:bins[0]被称为unsorted bin;bins[2-63]被称为samll bin;bins[64-127]被称为large bin。
- unsorted bin:顾名思义为无序箱子,其中盛放的内存块有大有小,且在arena机制中其优先级最低。
- samll bin:小箱子,每个相邻箱子之间的内存大小相差8bytes(32位机)和16bytes(64位机)。
- large bin:大箱子。
2.3. top
在一个进程中,top扮演堆顶的角色,在Linux中堆是向上增长,即想要堆进行增长就必须通过sbrk系统调用增加top的大小。
3. GLIBC正真的内存池
从上面基础知识介绍后 ,开始GLIBC内存池介绍。在GLIBC内存分箱管理前提下,每次用户请求malloc的内存块,经过free后并不是立马huan还给操作系统,而是直接缓存在arena中,如:fastbinsY、samll bin、large bin和unsorted bin(该顺序是按照分配和释放的优先级来排列)。
malloc过程优先级:fastbinsY——>samll bin——>large bin——>unsorted bin——>top(最后一步为增长堆空间满足用户请求)
free过程优先级:fastbinsY——>samll bin——>large bin——>unsorted bin——>top(释放堆空间)
从上面的优先级顺序可知,从系统重新获取内存的优先级最低,这样可以减少系统调用造成的性能开销;将内存还给系统的优先级也是最低的,只有满足当前释放的内存大于或等于top所对应的内存大小(即:user_size >= top_chunk_size),在GLIBC中top chunk大小一般为128Kb。
4. GLIBC堆空间增加与减少
在多线程主线程的堆空间主要通过sbrk系统调用进行扩展和缩减;而其他线程的堆空间主要通过mmap和munmap进行扩展和缩减。
4. malloc_trim
通过调用malloc_trim(0),可以将GLIBC缓存的内存直接还给操作系统。malloc_trim函数是直接修改top chunk的大小,将所有arena中fastbinsY对应的chunk块与相邻空闲chunk块进行合并,然后一次性返还给系统。