分箱式内存管理
Unsorted bin
Unsorted bin 可以看作是 small bins 和 large bins 的 cache,只有一个 unsorted bin,以双向链表管理空闲 chunk,空闲 chunk 不排序,所有的 chunk 在回收时都要先放到 unsorted bin 中,分配时,如果在 unsorted bin 中没有合适的 chunk,就会把 unsorted bin 中的所有 chunk 分别加入到所属的 bin 中,然后再在 bin 中分配合适的 chunk。Bins 数组中的元素 bin[1]用于存储 unsorted bin 的 chunk 链表头。
/* The otherwise unindexable 1-bin is used to hold unsorted chunks. */ #define unsorted_chunks(M) (bin_at(M, 1)) /* Conveniently, the unsorted bin can be used as dummy top on first call */ #define initial_top(M) (unsorted_chunks(M))
- unsorted_chunks(M):把 bin[1]设置为 unsorted bin 的 chunk 链表头。
- initial_top(M):对 top chunk 的初始化,也暂时把 top chunk 初始化为 unsort chunk,仅仅是初始化一个值而已,这个 chunk 的内容肯定不能用于 top chunk 来分配内存,主要原因是 top chunk 不属于任何 bin,但 ptmalloc 中的一些 check 代码,可能需要 top chunk 属于一个合法的 bin。
Fast bins
Fast bins 主要是用于提高小内存的分配效率,默认情况下,对于 SIZE_SZ 为 4B 的平台,小于 64B 的 chunk 分配请求,对于 SIZE_SZ 为 8B 的平台,小于 128B 的 chunk 分配请求,首先会查找 fast bins 中是否有所需大小的 chunk 存在(精确匹配),如果存在,就直接回。
Fast bins 可以看着是 small bins 的一小部分 cache,默认情况下,fast bins 只 cache 了 small bins 的前 7 个大小的空闲 chunk,也就是说,对于 SIZE_SZ 为 4B 的平台,fast bins 有 7 个 chunk 空闲链表(bin),每个 bin 的 chunk 大小依次为 16B,24B,32B,40B,48B,56B,64B;对于 SIZE_SZ 为 8B 的平台,fast bins 有 7 个 chunk 空闲链表(bin),每个 bin 的 chunk 大小依次为 32B,48B,64B,80B,96B,112B,128B。
- Fast bins 可以看着是 LIFO 的栈,使用单向链表实现。
typedef struct malloc_chunk* mfastbinptr; #define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx]) /* offset 2 to use otherwise unindexable first 2 bins */ #define fastbin_index(sz) \ ((((unsigned int)(sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2) /* The maximum fastbin request size we support */ #define MAX_FAST_SIZE (80 * SIZE_SZ / 4) #define NFASTBINS (fastbin_index(request2size(MAX_FAST_SIZE))+1) #define FASTBIN_CONSOLIDATION_THRESHOLD (65536UL) #ifndef DEFAULT_MXFAST #define DEFAULT_MXFAST (64 * SIZE_SZ / 4) #endif #define set_max_fast(s) \ global_max_fast = (((s) == 0) \ ? SMALLBIN_WIDTH: ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK)) #define get_max_fast() global_max_fast
- fastbin(ar_ptr, idx):根据 fast bin 的 index,获得 fast bin 的地址。Fast bins 的数字定义在 malloc_state 中。
- fastbin_index(sz):于获得 fast bin 在 fast bins 数组中的 index,由于 bin[0]和 bin[1]中的chunk不存在,所以需要减2,对于SIZE_SZ为4B的平台,将sz除以8减2得到fast bin index,对于 SIZE_SZ 为 8B 的平台,将 sz 除以 16 减去 2 得到 fast bin index。
- MAX_FAST_SIZE:根据 SIZE_SZ 的不同大小,定义 MAX_FAST_SIZE 为 80B 或是 160B。
- NFASTBINS:fast bins 数组的大小 NFASTBINS 为 10。
- FASTBIN_CONSOLIDATION_THRESHOLD:FASTBIN_CONSOLIDATION_THRESHOLD 为 64k,当每次释放的 chunk 与该 chunk 相邻的空闲 chunk 合并后的大小大于 64k 时,就认为内存碎片可能比较多了,就需要把 fast bins 中的所有 chunk 都进行合并,以减少内存碎片对系统的影响。
- DEFAULT_MXFAST:定义了默认的 fast bins 中最大的 chunk 大小,对于 SIZE_SZ 为 4B 的平台,最大 chunk 为 64B,对于 SIZE_SZ 为 8B 的平台,最大 chunk 为 128B。
- set_max_fast(s):ptmalloc 默认情况将全局变量 global_max_fast 设置为 DEFAULT_MXFAST,也就是设置 fast bins 中 chunk 的最大值。
- get_max_fast():获得这个全局变量 global_max_fast的值。
核心结构体分析
每个分配区是 struct malloc_state 的一个实例,ptmalloc 使用 malloc_state 来管理分配区,而参数管理使用 struct malloc_par,全局拥有一个唯一的 malloc_par 实例。
malloc_state
struct malloc_state { /* Serialize access. */ mutex_t mutex; /* Flags (formerly in max_fast). */ int flags; #if THREAD_STATS /* Statistics for locking. Only used if THREAD_STATS is defined. */ long stat_lock_direct, stat_lock_loop, stat_lock_wait; #endif /* 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; #ifdef PER_THREAD /* Linked list for free arenas. */ struct malloc_state *next_free; #endif /* Memory allocated from the system in this arena. */ INTERNAL_SIZE_T system_mem; INTERNAL_SIZE_T max_system_mem; };
- Mutex:用于串行化访问分配区,当有多个线程访问同一个分配区时,第一个获得这个 mutex 的线程将使用该分配区分配内存,分配完成后,释放该分配区的 mutex,以便其它线程使用该分配区。
- Flags:记录了分配区的一些标志,bit0 用于标识分配区是否包含至少一个 fast bin chunk,bit1 用于标识分配区是否能返回连续的虚拟地址空间。
- fastbinsY:拥有 10(NFASTBINS)个元素的数组,用于存放每个 fast chunk 链表头指针,所以 fast bins 最多包含 10 个 fast chunk 的单向链表。
- top:一个 chunk 指针,指向分配区的 top chunk。
- last_remainder:是一个 chunk 指针,分配区上次分配 small chunk 时,从一个 chunk 中分裂出一个 small chunk 返回给用户,分裂后的剩余部分形成一个 chunk,last_remainder 就是指向的这个 chunk。
- binmap:一个 int 数组,ptmalloc 用一个 bit 来标识该 bit 对应的 bin 中是否包含空闲 chunk。
#define FASTCHUNKS_BIT (1U) #define have_fastchunks(M) (((M)->flags & FASTCHUNKS_BIT) == 0) #ifdef ATOMIC_FASTBINS #define clear_fastchunks(M) catomic_or (&(M)->flags, FASTCHUNKS_BIT) #define set_fastchunks(M) catomic_and (&(M)->flags, ~FASTCHUNKS_BIT) #else #define clear_fastchunks(M) ((M)->flags |= FASTCHUNKS_BIT) #define set_fastchunks(M) ((M)->flags &= ~FASTCHUNKS_BIT) #endif
- have_fastchunks(M):如果返回值为 1,表示分配区有 fast bin,如果返回值为 0,表示没有 fast bin。
- clear_fastchunks(M):将 bit0 标志位置 1,及无 fast bin。
- set_fastchunks(M):将 bit0 标志位置 0,及有 fast bin。
- 初始化完成后的 malloc_state 实例中,flags 值为 0,表示该分配区中有 fast chunk,但实际上没有,试图从 fast bins 中分配chunk 都会返回 NULL,在第一次调用函数 malloc_consolidate()对 fast bins 进行 chunk 合并时,如果 max_fast 大于 0,会调用 clear_fastchunks 宏,标志该分配区中已经没有 fast chunk,因为函数 malloc_consolidate()会合并所有的 fast bins 中的 chunk。clear_fastchunks 宏只会在函数 malloc_consolidate()中调用。当有 fast chunk 加入 fast bins 时,就是调用 set_fastchunks 宏标
识分配区的 fast bins 中存在 fast chunk。
#define NONCONTIGUOUS_BIT (2U) #define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0) #define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0) #define set_noncontiguous(M) ((M)->flags |= NONCONTIGUOUS_BIT) #define set_contiguous(M) ((M)->flags &= ~NONCONTIGUOUS_BIT)
- contiguous(M):返回值为 1,表示 MORCORE 返回连续虚拟地址空间;返回值为 0,表示 MORCORE 返回非连续虚拟地址空间。
- noncontiguous(M):返回值为 1,表示 MORCORE 返回非连续虚拟地址空间;返回值为 0,表示 MORCORE 返回连续虚拟地址空间。
- set_noncontiguous(M):将 bit1 标志位置 1,表示 MORCORE 返回非连续虚拟地址空间。
- set_contiguous(M):将 bit1 标志位置 0,表示 MORCORE 返回连续虚拟地址空间。
内容来源
《Glibc内存管理》