struct malloc_chunk { /*前一chunk如果是空闲,则表示它的大小,否则被其用来存储数据*/ INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free).*/ /* 当前块的大小 size的对齐为0x10 后三位为AWP标志位 */ INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead.*/
/*双向链表*/ struct malloc_chunk* fd; /* double links -- used only if free. 前向指针*/ struct malloc_chunk* bk; /*后向指针*/ /*最小的chunk大小必须包含前四个域*/ /* Only used for large blocks: pointer to next larger size. */ /*这两个结构在large block中使用*/ struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ struct malloc_chunk* bk_nextsize; };
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk, if unallocated (P clear) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of chunk, in bytes |A|M|P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | User data starts here... . . . . (malloc_usable_size() bytes) . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | (size of chunk, but used for application data) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of next chunk, in bytes |A|0|1| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of previous chunk, if unallocated (P clear) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `head:' | Size of chunk, in bytes |A|0|P| mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Forward pointer to next chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Back pointer to previous chunk in list | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Unused space (may be 0 bytes long) . . . . | nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ `foot:' | Size of chunk, in bytes | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Size of next chunk, in bytes |A|0|0| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/* Flags (formerly in max_fast). */ /*flags 记录了分配区的一些标志*/ 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;/*是否存在fastbin*/
/* Base of the topmost chunk -- not otherwise kept in a bin */ mchunkptr top;/*记录top chunk*/
/* 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];/* unsorted bin small bin large bin */
/* Bitmap of bins */ unsigned int binmap[BINMAPSIZE];/*标识某个bin是否空的map */
/* Linked list */ struct malloc_state *next;/*与下一个malloc_state形成双链表*/
/* 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; };
#define NBINS 128 /*bin的总数 unsorted bin(1)+smallbin(62)+largebin(63)+1 */ malloc_state{ ... /* Normal bins packed as described above */ mchunkptr bins[NBINS * 2 - 2];/* unsorted bin small bin large bin */ ... }
small bins总共有62组,每组中(或者说每个bin中)的chunk都是相同的大小,组之间以0x10字节作为大小间距(x86系统下为8字节),也就是说每个bin本身是一个先进先出的双向链表。而前面说过bins中从bin[2]开始每一对元素表示一个bin,也就是说small bin在bins中的存放就是从bin[2]bin[3]到bin[124]bin[125]。
/* The smallest possible chunk */ /*定义了最小的chunk,这里可以看到最小的chunk大小为chunk到fd_nextsize偏移 *所以最小的chunk必须有prev_size size fd bk 四个部分 即0x20 */ #define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
large bin
large bin总共有63组,与small bin不同的是,每个large bin中的bin链并不是都存放着相同大小的chunk(其实这个可以想到,因为chunk变大,这样存放会导致bin的内存开销变大)。每个large bin均是一个先进先出的双向链表,在bins数组中是从bin[126]bin[127]到bin[250]bin[251](这里其实有个疑惑,bins总共254个元素,看来最后一组bin[252]bin[253]好像并没使用?)。
/* ... 32 bins of size 64 0x40 16 bins of size 512 0x200 8 bins of size 4096 0x1000 4 bins of size 32768 0x8000 2 bins of size 262144 0x40000 1 bin of size what's left ... */
这一段是什么意思呢,也就是说,对前32个large bin 他们的组内间隔最大均为0x40,即第一个largebinbin[126]bin[127]范围就应该是[0x400,0x440],后面的以此类推。
To help compensate for the large number of bins, a one-level index structure is used for bin-by-bin searching. `binmap' is a bitvector recording whether bins are definitely empty so they can be skipped over during during traversals. The bits are NOT always cleared as soon as bins are empty, but instead only when they are noticed to be empty during traversal in malloc.
#if USE_TCACHE /* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */ # define TCACHE_MAX_BINS 64/*tcache链数量*/ # define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)
/* Only used to pre-fill the tunables. */ # define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
/* When "x" is from chunksize(). */ # define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT) /* When "x" is a user-provided size. */ # define usize2tidx(x) csize2tidx (request2size (x))
/* With rounding and alignment, the bins are... idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit) idx 1 bytes 25..40 or 13..20 idx 2 bytes 41..56 or 21..28 etc. */
/* This is another arbitrary limit, which tunables can change. Each tcache bin will hold at most this number of chunks. */ /*每个tcache链上chunk的最大值*/ # define TCACHE_FILL_COUNT 7
/* Maximum chunks in tcache bins for tunables. This value must fit the range of tcache->counts[] entries, else they may overflow. */ # define MAX_TCACHE_COUNT UINT16_MAX #endif ... #if USE_TCACHE
/* We overlay this structure on the user-data portion of a chunk when the chunk is stored in the per-thread cache. */ /*tcache entry结构体 用于链接空闲的chunk结构体,其中的next指针指向下一个相同大小的chunk*/ typedef struct tcache_entry { struct tcache_entry *next;/* next指向的是chunk的user data的开始,而非chunk的开始*/ /* This field exists to detect double frees. */ /*这个域用来检测double free*/ struct tcache_perthread_struct *key; } tcache_entry;
/* There is one of these for each thread, which contains the per-thread cache (hence "tcache_perthread_struct"). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */ /*每个thread维护一个tcache_prethread_struct*/ typedef struct tcache_perthread_struct { uint16_t counts[TCACHE_MAX_BINS];//计数器 记录tcache_entry链上空闲chunk的数目,每条链上最多有7个chunk tcache_entry *entries[TCACHE_MAX_BINS];//tcache_entry数组,每个都是以单向链表的方式链接了大小相同的的处于空闲状态或者说free 后的chunk } tcache_perthread_struct;
/* Caller must ensure that we know tc_idx is valid and there's room for more chunks. */ /*将一个chunk放回tcache中*/ static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) { /*chunk转为mem指针*/ tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
/* Mark this chunk as "in the tcache" so the test in _int_free will detect a double free. */ //设置放回头部 e->key = tcache;
/* Disable the tcache and prevent it from being reinitialized. */ tcache = NULL; tcache_shutting_down = true;
/* Free all of the entries and the tcache itself back to the arena heap for coalescing. */ for (i = 0; i < TCACHE_MAX_BINS; ++i) { while (tcache_tmp->entries[i]) { tcache_entry *e = tcache_tmp->entries[i]; tcache_tmp->entries[i] = e->next; __libc_free (e); } }