1. struct _heap_info, struct malloc_state和struct malloc_chunk
typedef struct _heap_info
{
mstate ar_ptr; /* Arena for this heap. */
struct _heap_info *prev; /* Previous heap. */
size_t size; /* Current size in bytes. */
size_t mprotect_size; /* Size in bytes that has been mprotected
PROT_READ|PROT_WRITE. */
/* Make sure the following data is properly aligned, particularly
that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of
MALLOC_ALIGNMENT. */
char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK];
} heap_info;
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;
};
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size;//物理地址上相邻的前一个chunk的大小
INTERNAL_SIZE_T mchunk_size;//该chunk大小
struct malloc_chunk* fd;//被分配时:用户数据的起始地址;未被分配时:指向前一个空闲chunk
struct malloc_chunk* bk;//指向后一个空闲
struct malloc_chunk* fd_nextsize; //指向前一个与本chunk大小不同的chunk
struct malloc_chunk* bk_nextsize; //指向后一个大小不同chunk
};
关系: _heap_info -> malloc_state(arena) 管理malloc_chunk链表。_heap_info 对应一个堆段,和arena可能多对一。
当线程首次调用malloc时会先检查创建的arena有没有到上限,如果没有则创建,如果达到了则试图加锁一个现存的arena。
只有主arena才能调用brk在堆段上申请空间,而失败时才会调用mmap,分arena空间不足时只能通过mmap创建新heap(注意和linux内存空间上的heap是两个概念这里),通过mmap申请的空间在free时会直接munmap释放。
2. fastbins和bins
一个bin就是一个由malloc_chunk组成的链表,且都是未分配的chunk,这就是为什么其fd成员只有在未分配状态下才是一个指向chunk的指针。
fastbins
chunk size: 32 - 128 byte
链表数:10
中保存32-128 byte(包含结构体成员)的chunk,单链表,LIFO;注意,malloc_state中fastbinsY中保存的是多条fastbin链表。一种大小的一条,小size 的链表头在前,大的在后(类似STL的二级空间allocater)。
bins
unsorted bin
chunk size: 大于128,free后未被整理的chunk
链表数:1
small bin
chunk size: 2 * SIZE_SZ * index, 最大为2*4*63 = 504, 64位下为1008
链表数:62
large bin
chunk size: 分为6组,每组的每条链表代表一个区间,组与组之间区别在于不同的区间步长。最大无上限。
链表数:32 + 16 + 8 + 4 + 2 + 1 = 63
3. malloc(_int_malloc)的过程
bins的管理其实不涉及到内核操作,即没有系统调用,malloc优先查找的是bin中的chunk,若没有合适的chunk则会到堆顶的top_chunk(向内核申请的但没有使用也没有进入bin的空间),若top_chunk不够才会调用brk/mmap扩大top_chunk。而被free的chunk的指针会交由对应大小的bin保管。malloc不会主动将top_chunk的空间划分给bin,也就是说bin中的chunk都是从回收中来的。
执行顺序:
1. 寻找可用的arena
2. 判断大小是否符合fastbin,smallbin以及查找有无可用chunk;
3. 如果没返回,说明没找到或大小超过smallbin不符,执行malloc_consolidate(整合,合并fast_bins中相邻chunk,并将unsorted_bin中的chunk分到其他bin中),整理以当前arena为单位
具体实现看这篇https://blog.csdn.net/qq_41453285/article/details/97627411
4. 进入循环依次查找unsortedbin(包含整合),smallbin, largebin, topchunk,其中对前三者的搜索如果找不到相等的块则找比申请大小大的chunk进行切割; 最后判断top chunk,如果不够再调用sysmalloc扩大topchunk, 或直接mmap一个chunk给用户(若请求大于128K).
*可以看出整个过程只有最后的sysmalloc才经过内核,而topchunk以下的空间其实已经算是合法的用户地址空间了,这就是为什么没有malloc的一些空间也能访问而不产生运行时错误。
*last_remainder指向切割后的余下部分,当申请大小为smallbin时优先用它来切割,为了提高小内存的空间局部性。
4. 还给操作系统
1. 如果是mmap创建的chunk直接munmap。
2.heap则需要在free掉top chunk相邻的chunk、将chunk与top chunk合并时,若top chunk大于一个阈值则交还一部分给操作系统。