struct malloc_state {
mfastbinptr fastbinsY[NFASTBINS];
mchunkptr top;
mchunkptr last_remainder;
mchunkptr bins[NBINS * 2 - 2];
unsigned int binmap[BINMAPSIZE];
};-
fastbinsY[NFASTBINS]:fastbinsY表示fastbins,数组长度为9,用于快速分配小块内存(64位系统通常为32-160字节)。 -
top:表示top chunk,当其他bins中没有合适大小的chunk时,会从top chunk中裁剪出合适的chunk用于内存分配。 -
last_remainder: 指向上次裁剪smallbins链表中chunk剩余的chunk。
-
bins[NBINS * 2 – 2]:数组长度为254,包含三种类型的bin:
-
unsortedbin:一个特殊的bin,刚释放的chunk会先放入这里,后续从unsortedbin转入其他bin。 -
smallbins:62个双向链表,每个链表管理固定大小的chunk(64 位系统通常为:32-1024 字节,16 字节步进),。 -
largebins:63个双向链表,每个链表管理一个范围大小的chunk(64位系统通常大于1024字节)。
-
binmap[BINMAPSIZE]: 128位位图,用于快速查找smallbins和largebins中可用的chunk,避免遍历整个bins数组。
struct malloc_chunk {
long int mchunk_prev_size;
long int mchunk_size;
struct malloc_chunk* fd;
struct malloc_chunk* bk;
struct malloc_chunk* fd_nextsize;
struct malloc_chunk* bk_nextsize;
};
-
mchunk_prev_size:8字节,简称prev_size,表示前一个chunk的大小。
-
mchunk_size:8字节,简称size,表示当前chunk的大小。size的低3位是标志位,3个标志说明如下: -
PREV_INUSE (P位):最低位,表示前一个chunk是否在使用中,0表示前一个chunk空闲,1表示前一个chunk已分配。 -
IS_MMAPPED (M位):第二低位,表示当前chunk是否通过mmap分配。 -
NON_MAIN_ARENA (A位):第三低位,表示当前chunk是否属于主线程的分配区(malloc_state)。 -
fd:8字节,指向下一个空闲chunk的指针。这个字段仅在当前chunk是空闲时有效。如果当前chunk已分配,那么fd字段被复用为可用内存(8字节)。
-
bk:指向前一个空闲chunk的指针。该字段和fd功能类似。 -
fd_nextsize:8字节,largebins中指向下一个比当前chunk小的空闲chunk。用于largebins快速查找不同大小的空闲chunk,提高分配效率,如果当前chunk是已分配的,那么fd_nextsize字段被复用为可用内存(8字节)。 -
bk_nextsize:8字节,largebins中指向前一个比当前chunk大的空闲chunk,和fd_nextsize功能类似。
top chunk是堆顶的空闲chunk,它始终位于堆的末尾。当内存池中的现有的chunk无法满足分配请求时,top chunk可以扩展以提供更多的内存。当top chunk的空间不足以满足分配请求时,分配器会通过brk或mmap向操作系统请求更多内存,从而扩展top chunk的大小。
由于chunk可以很方便地进行合并和裁剪,top chunk可以裁剪出合适的chunk。当top chunk的前一个chunk空闲时,top chunk可以和前一个chunk执行一次向前合并操作,形成一个更大内存的top chunk。
#define largebin_index_64(sz)
(((((unsigned long) (sz)) >> 6) <= 48) ? 48 (((unsigned long) (sz)) >> 6) :
((((unsigned long) (sz)) >> 9) <= 20) ? 91 (((unsigned long) (sz)) >> 9) :
((((unsigned long) (sz)) >> 12) <= 10) ? 110 (((unsigned long) (sz)) >> 12) :
((((unsigned long) (sz)) >> 15) <= 4) ? 119 (((unsigned long) (sz)) >> 15) :
((((unsigned long) (sz)) >> 18) <= 2) ? 124 (((unsigned long) (sz)) >> 18) :
126)-
步骤1:根据申请的chunk大小计算出所在bins数组索引值。 -
步骤2:根据bins数组索引值计算出binmap数组索引。 -
步骤3:根据binmap数组索引找到binmap数组元素。 -
步骤4:通过bins数组索引计算出其在binmap中对应的位。
void *__libc_malloc(size_t bytes)
{
mstate ar_ptr;
void *victim;
/* 获取malloc_state分配区 */
arena_get (ar_ptr, bytes);
/* 从分配区分配内存核心代码 */
victim = _int_malloc (ar_ptr, bytes);
......
return victim;
}
static void *_int_malloc (mstate av, size_t bytes)
{
INTERNAL_SIZE_T nb;
/* 用户申请的内存大小转换为chunk大小(16字节对齐) */
nb = checked_request2size (bytes);
/* chunk小于160字节,从fastbins分配内存 */
if(nb <= get_max_fast())
{
/* 通过chunk大小获取fastbin */
idx = fastbin_index (nb);
mfastbinptr *fb = &fastbin (av, idx);
mchunkptr pp;
victim = *fb;
/* 单链表操作,取出待分配的chunk */
REMOVE_FB (fb, pp, victim);
/* 计算chunk可用内存区域起始地址 */
void *p = chunk2mem (victim);
/* 分配内存,返回起始地址*/
return p;
}
/* chunk小于1024字节,从smallbins分配内存 */
if (in_smallbin_range (nb))
{
/* 通过chunk大小获取smallbin */
idx = smallbin_index (nb);
bin = bin_at (av, idx);
/* 分配内存 */
void *p = chunk2mem (victim);
return p;
}
/* 从unsortedbin分配chunk,循环10000次 */
for (;; )
{
victim = unsorted_chunks(av)->bk;
while (victim != unsorted_chunks(av)
{
/* 尝试从last_remainer chunk分配内存 */
if (in_smallbin_range(nb) &&
bck == unsorted_chunks (av) &&
victim == av->last_remainder &&
size > nb MINSIZE)
{
1.裁剪last_remainer chunk
2.将裁剪后剩余chunk插入unsortedbin
3.分配内存(裁剪后chunk)
return p;
}
1.如果chunk和unsortedbin chunk匹配,直接分配内存
2.否则将chunk转移至smallbins或larginbins
}
}
/* 尝试从largebins分配内存*/
if (!in_smallbin_range (nb))
{
victim = victim->bk_nextsize;
/* 通过bk_nextsize快速索引largebins */
while (size = chunksize (victim)) < nb)
victim = victim->bk_nextsize;

.......
/* 将chunk从largebins链表中移除*/
unlink_chunk (av, victim);
1.裁剪chunk
2.裁剪剩余chunk插入unsortedbin
3.分配内存(裁剪后chunk)
return p;
}
/* 轮询bin位图准备工作,获取位图数组索引、数组元素、位 */
bin = bin_at (av, idx);
block = idx2block (idx);
map = av->binmap[block];
bit = idx2bit (idx);
/* 轮询bin位图 */
for (;; )
{
1.如果位图对应的位置1,获取对应的bin
2.从bin中取出chunk
3.大的chunk需要裁剪,剩余chunk插入unsortedbin
4.分配内存(裁剪后chunk)
return p;
}
use_top:
/* 从top chunk分配chunk */
victim = av->top;
size = chunksize (victim);
/* 如果top chunk满足内存分配要求*/
if (size >= nb MINSIZE)
{
1.裁剪top chunk
2.更新top指针
3.分配内存(裁剪后chunk)
return p;
}
/* 如果top chunk不满足内存分配要求 */
else
{
/* 通过mmap进行内存分配或者通过sbrk、brk扩容 */
void *p = sysmalloc (nb, av);
/* 分配内存 */
return p;
}
}void __libc_free (void *mem)
{
mstate ar_ptr;
mchunkptr p;
/* free(0)直接返回 */
if (mem == 0)
return;
/* 将用户内存区域转换为chunk */
p = mem2chunk (mem);
/* 如果chunk是通过mmap分配,则通过munmap释放*/
if (chunk_is_mmapped (p))
{
munmap_chunk (p);
}
/* 释放chunk至malloc_state分配区 */
else
{
/* 获取malloc_state分配区 */
ar_ptr = arena_for_chunk (p);
/* 释放chunk核心代码 */
_int_free (ar_ptr, p, 0);
}
}
static void _int_free (mstate av, mchunkptr p, int have_lock)
{
INTERNAL_SIZE_T size;
mfastbinptr *fb;
/* 获取chunk大小 */
size = chunksize (p);
/* chunk释放至fastbins */
if (size <= get_max_fast ())
{
1.通过chunk大小找到fastbin
2.将chunk插入fastbin单链表
return;
}
/* 该情况将chunk释放至unsortedbin */
elseif (!chunk_is_mmapped(p)) {
/* 释放chunk至unsortedbin,
注意:释放过程中需要执行向前合并和向后合并操作!!! */
_int_free_merge_chunk (av, p, size);
}
/* chunk通过mmap分配 */
else {
/* 通过munmap释放 */
munmap_chunk (p);
}
}