glibc2.23堆源码解读
libc2.23
采用glibc2.23的源码,故没有libc2.32的Tcache。主要从_int_malloc以及_int_free两个函数进行解读。
Malloc
static void *
_int_malloc (mstate av, size_t bytes){//参数为指针av,与申请空间bytes
INTERNAL_SIZE_T nb; /* normalized request size */
unsigned int idx; /* associated bin index */
mbinptr bin; /* associated bin */
mchunkptr victim; /* inspected/selected chunk */
INTERNAL_SIZE_T size; /* its size */
int victim_index; /* its bin index */
mchunkptr remainder; /* remainder from a split */
unsigned long remainder_size; /* its size */
unsigned int block; /* bit map traverser */
unsigned int bit; /* bit map traverser */
unsigned int map; /* current word of binmap */
mchunkptr fwd; /* misc temp for linking */
mchunkptr bck; /* misc temp for linking */
//定义基础变量
const char *errstr = NULL;
/*
通过添加SIZE_SZ字节将请求大小转换为内部形式
开销加上可能更多,以获得必要的对齐和/或
要获得至少MINSIZE的大小,请使用最小的可分配
大小此外,checked_request2size陷阱(返回0)请求大小
这是如此之大,以至于当填充和
对齐的。
*/
checked_request2size (bytes, nb);//检测bytes大小,并赋值到nb上.且经历request2size函数(该函数作用为附加上chunk头的bytes)
//此时nb为内核态的chunk大小
/* There are no usable arenas. Fall back to sysmalloc to get a chunk from
mmap. */
if (__glibc_unlikely (av == NULL))//如果指针av为空,调用该函数
{
void *p = sysmalloc (nb, av);//调用内核函数申请空间
if (p != NULL)
alloc_perturb (p, bytes);//对内存进行初始化,内含memset函数
return p;//返回指针p
}
/*
尺寸符合快速箱子的要求,首先检查相应的箱子。
即使av尚未初始化,此代码也可以安全执行,因此我们
可以不用检查就可以尝试,这样可以在快速路径上节省一些时间。
*/
if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))//如果nb小于最大fastbin的maxsize话,则申请fast大小的chunk
{
idx = fastbin_index (nb);//读取nb所在链队的序号赋值到idx
mfastbinptr *fb = &fastbin (av, idx);//取出fastbin链中的元素
mchunkptr pp = *fb;//将指针fb封装成结构体
do
{
victim = pp;//将pp赋值到victim
if (victim == NULL)//如果为空跳出循环
break;
}
while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))
!= victim);//进行原子比较
if (victim != 0)//通过原子比较
{
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))//此时再次进行比较bytes是否与最开始的idx序列号相等
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);//执行报错函数
return NULL;//返回NULL
}
check_remalloced_chunk (av, victim, nb);//检查chunk
void *p = chunk2mem (victim);//将大小转换为用户态大小的指针
alloc_perturb (p, bytes);//对其进行初始化
return p;//返回指针
}
}
/*
如果有小请求,请检查常规垃圾箱。因为这些“小箱子”
每种尺寸保持一个,无需在箱子内搜索。
(对于大型请求,我们需要等待未排序的块被删除
经过处理以找到最合适的。但对于小的,适合是准确的
无论如何,我们现在可以检查一下,哪个更快。)
*/
if (in_smallbin_range (nb))//判断大小nb是否存在于smallbin链的范围内
{
idx = smallbin_index (nb);//返回nb处于smallbin中的序列号
bin = bin_at (av, idx);
if ((victim = last (bin)) != bin)//判断bin的后继与bin是否相等
{
if (victim == 0) /* initialization check */ //不相等
malloc_consolidate (av);//将fastbin中的空闲chunk合并整理到unsorted_bin中以及进行初始化堆的工作
else
{
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))//如果此时发现与刚开始的bk后继不相等报错
{
errstr = "malloc(): smallbin double linked list corrupted";
goto errout;//跳转到报错函数
}
set_inuse_bit_at_offset (victim, nb);//设置标记位
bin->bk = bck;
bck->fd = bin;//将victim从bin链中摘除掉,即返回给用户chunk
if (av != &main_arena)//如果av不存在于main_arena中,设置标记位
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);//检测chunk
void *p = chunk2mem (victim);//将指针转换位用户态指针
alloc_perturb (p, bytes);//初始化chunk
return p;//返回指针
}
}
}
/*
如果这是一个大请求,请在继续之前合并fastbins。
虽然之前杀掉所有的快速垃圾箱看起来有些过分
即使看到是否有可用空间,这也可以避免
通常与fastbins相关的碎片问题。
此外,在实践中,程序往往会有小规模或小规模的运行
请求量大,但混合请求的频率较低,因此不需要整合
在大多数程序中经常调用这些函数。以及
它在中经常被调用,否则会变得支离破碎。
*/
else //进入largebin中
{
idx = largebin_index (nb);//判断nb处于largebin中的序列号
if (have_fastchunks (av))//返回值为1,表示存在fastbin
malloc_consolidate (av);//进行堆的consolidate,合并
}
/*
处理最近释放或剩余的块,仅当
它是完全合适的,或者,如果这是一个小请求,则该块是来自
最近的非精确拟合。将其他遍历的块放入
箱子。请注意,此步骤是任何例程中唯一需要块放在箱子里。
这里需要外部循环,因为我们可能直到
在malloc快结束的时候,我们本应该合并的,所以必须合并
执行此操作并重试。这种情况最多发生一次,而且只有在我们愿意的时候才会发生
否则需要扩展内存以满足“小”请求。
*/
for (;; )//进入大循环
{
int iters = 0;
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))//存在unsorted_bin则进入循环
{
bck = victim->bk;
if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (victim->size > av->system_mem, 0))
malloc_printerr (check_action, "malloc(): memory corruption",
chunk2mem (victim), av);//进入报错函数
size = chunksize (victim);//获取chunk大小
/*
如果是小请求,请尝试使用最后的余数(如果是
只有未排序的存储箱中的块。这有助于促进地方性
连续小请求的运行。这是唯一的
最佳拟合的例外情况,仅在存在
不适合一小块。
*/
if (in_smallbin_range (nb) &&
bck == unsorted_chunks (av) &&
victim == av->last_remainder &&
(unsigned long) (size) > (unsigned long) (nb + MINSIZE))//判断是否满足切割条件,切割last_remainder
{
/* 拆分并重新连接剩余部分 */
remainder_size = size - nb;//修改remainder的size大小
remainder = chunk_at_offset (victim, nb);//修改remainder的指针
unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
av->last_remainder = remainder;
remainder->bk = remainder->fd = unsorted_chunks (av);
if (!in_smallbin_range (remainder_size))//如果不在smallbin范围中,则执行以下语句
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));//设置victim的标记位
set_head (remainder, remainder_size | PREV_INUSE);//设置remainder标记位
set_foot (remainder, remainder_size);//设置remainder的大小
check_malloced_chunk (av, victim, nb);//检测chunk
void *p = chunk2mem (victim);//转换为用户所使用的指针,隐藏头部信息
alloc_perturb (p, bytes);//初始化chunk
return p;//返回指针
}
/* 从未排序的列表中删除 */
unsorted_chunks (av)->bk = bck;//修改unsorted_bin链中的节点
bck->fd = unsorted_chunks (av);//即从unsorted_bin链中删除victim
/* 如果完全合适,请立即服用,而不是装箱 */
if (size == nb)//如果size与申请nb大小相等
{
set_inuse_bit_at_offset (victim, size);//设置使用inuse标志位
if (av != &main_arena)//如果victim不存在main_arena中
victim->size |= NON_MAIN_ARENA;//设置N标志位
check_malloced_chunk (av, victim, nb);//检测chunk
void *p = chunk2mem (victim);//转换为用户所使用的指针,隐藏头部信息
alloc_perturb (p, bytes);//初始化chunk
return p;//返回指针
}
/* 将块放入垃圾箱中 */
if (in_smallbin_range (size))//如果处于smallbin链范围大小之中,则放入smallbin中
{
victim_index = smallbin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;
}
else//否则处于largebin链范围中,则放入largebin中
{
victim_index = largebin_index (size);
bck = bin_at (av, victim_index);
fwd = bck->fd;//为bck的前驱结点
/* maintain large bins in sorted order */
if (fwd != bck)//表明链表中存在不只一个chunk
{
/* 或使用位速度比较 */
size |= PREV_INUSE;
/* 如果小于最小值,则旁路回路位于下方 */
assert ((bck->bk->size & NON_MAIN_ARENA) == 0);//判断N标记位是否为0,即判断chunk是否位于main_arena中
if ((unsigned long) (size) < (unsigned long) (bck->bk->size))//如果发现满足该条件,说明该chunk可直接填充到largebin中
{
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else//否则需要循环判断插入位置
{
assert ((fwd->size & NON_MAIN_ARENA) == 0);//判断chunk是否位于main_arena中
while ((unsigned long) size < fwd->size)//循环判断放入链表中位置
{
fwd = fwd->fd_nextsize;//判断victim应处于的位置
assert ((fwd->size & NON_MAIN_ARENA) == 0);//判断chunk是否位于main_arena中
}
if ((unsigned long) size == (unsigned long) fwd->size)//如果恰巧相等
/* 始终插入第二个位置 */
fwd = fwd->fd;
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;//进行拼接链表
}
bck = fwd->bk;
}
}
else//此时表明该链表中只存在一个chunk
victim->fd_nextsize = victim->bk_nextsize = victim;//直接放入chunk即可
}
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;//重新构造链表
#define MAX_ITERS 10000
if (++iters >= MAX_ITERS)
break;
}
/*
如果请求较大,请扫描中的当前bin块
按顺序排序以找到适合的最小值。为此使用跳过列表。
*/
if (!in_smallbin_range (nb))//如果nb不存在smallbin范围中
{
bin = bin_at (av, idx);
/* 如果空块或最大块太小,则跳过扫描 */
if ((victim = first (bin)) != bin &&
(unsigned long) (victim->size) >= (unsigned long) (nb))//判断是否为largebin范围中
{
victim = victim->bk_nextsize;
while (((unsigned long) (size = chunksize (victim)) <
(unsigned long) (nb)))//循环寻找合适的chunk
victim = victim->bk_nextsize;
/* 避免删除大小的第一个条目,以便跳过
列表不必重新路由. */
if (victim != last (bin) && victim->size == victim->fd->size)//判断是否前一个结点的size与申请空间相等
victim = victim->fd;
remainder_size = size - nb;
unlink (av, victim, bck, fwd);//进行合并
/* 耗尽 */
if (remainder_size < MINSIZE)//如果小于MINSIZE
{
set_inuse_bit_at_offset (victim, size);//设置inuse标记位
if (av != &main_arena)//判断是否位于main_arena中
victim->size |= NON_MAIN_ARENA;
}
/* 分裂 */
else
{
remainder = chunk_at_offset (victim, nb);
/* 我们不能假定未排序的列表为空,因此
必须在此处执行完整的插入. */
bck = unsorted_chunks (av);//取出unsorted_bin链
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))//判断是否完整
{
errstr = "malloc(): corrupted unsorted chunks";
goto errout;
}
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
fwd->bk = remainder;//重新放置结点到unsorted_bin链中
if (!in_smallbin_range (remainder_size))//如果处于smallbin范围中
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));//设置victim的头部信息
set_head (remainder, remainder_size | PREV_INUSE);//设置remainder的头部信息
set_foot (remainder, remainder_size);//设置remainder的prev_size
}
check_malloced_chunk (av, victim, nb);//检测chunk
void *p = chunk2mem (victim);//转换为用户chunk
alloc_perturb (p, bytes);//初始化chunk
return p;//返回指针
}
}
/*
从下一个最大的垃圾箱开始,通过扫描垃圾箱来搜索一个垃圾块
箱子此搜索严格按照最佳匹配进行;i、 e.最小的
(领带的使用量大约是最近最少的)大块
选择合适的。
位图避免了检查大多数块是否为非空。
在预热阶段跳过所有箱子的特殊情况
当还没有返回块时,它的速度比看起来的要快。
*/
++idx;
bin = bin_at (av, idx);
block = idx2block (idx);//计算出该 bin 在 binmap 对应的 bit 属于哪个 block
map = av->binmap[block];
bit = idx2bit (idx);
for (;; )
{
/* 如果该块中没有更多的设置位,则跳过该块的其余部分. */
if (bit > map || bit == 0)
{
do
{
if (++block >= BINMAPSIZE) /* 垃圾桶外 */
goto use_top;
}
while ((map = av->binmap[block]) == 0);
bin = bin_at (av, (block << BINMAPSHIFT));
bit = 1;
}
/* 使用设定的位前进到料仓。一定有. */
while ((bit & map) == 0)
{
bin = next_bin (bin);
bit <<= 1;
assert (bit != 0);
}
/* 检查垃圾箱。它可能是非空的 */
victim = last (bin);
/* 如果出现假警报(空箱子),请清除该位. */
if (victim == bin)
{
av->binmap[block] = map &= ~bit; /* Write through */
bin = next_bin (bin);
bit <<= 1;
}
else
{
size = chunksize (victim);
/* 我们知道这个箱子里的第一块足够大,可以使用. */
assert ((unsigned long) (size) >= (unsigned long) (nb));//判断size是否大于申请空间
remainder_size = size - nb;
/* unlink */
unlink (av, victim, bck, fwd);//进行合并
/* Exhaust */
if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset (victim, size);//设置inuse标记位
if (av != &main_arena)//判断chunk是否位于main_arena中
victim->size |= NON_MAIN_ARENA;//设置N标记位
}
/* Split */
else
{
remainder = chunk_at_offset (victim, nb);
/* 我们不能假定未排序的列表为空,因此
必须在此处执行完整的插入. */
bck = unsorted_chunks (av);
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))
{
errstr = "malloc(): corrupted unsorted chunks 2";
goto errout;
}
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
fwd->bk = remainder;//重新排布结点
/* advertise as last remainder */
if (in_smallbin_range (nb))//如果nb满足smallbin范围之中
av->last_remainder = remainder;//将last_remainder重新指向remainder
if (!in_smallbin_range (remainder_size))//如果剩余部分不在smallbin范围中,将nextsize设置为NULL
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
set_foot (remainder, remainder_size);
}
check_malloced_chunk (av, victim, nb);//检测chunk
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);//初始化chunk
return p;//返回指针
}
}
use_top://top chunk
/*
如果足够大,将内存末尾的块分割开
(在av->top中保持)。请注意,这符合最佳拟合
搜索规则。实际上,av->top被视为更大(因此
因为它可以
根据需要扩展到尽可能大(直到系统
限制)
我们要求av->top始终存在(即大小>=
MINSIZE)在初始化之后,如果不是这样
被当前请求耗尽后,它将被补充。(主要
确保它存在的原因是我们可能需要MINSIZE空间
在sysmalloc中放置围栏柱。)
我们要求av->top始终存在(即大小>=
MINSIZE)在初始化之后,如果不是这样
被当前请求耗尽后,它将被补充。(主要
确保它存在的原因是我们可能需要MINSIZE空间
在sysmalloc中放置围栏柱。)
*/
victim = av->top;
size = chunksize (victim);
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))//判断是否满足申请空间大小,如果满足则进行切割
{
remainder_size = size - nb;
remainder = chunk_at_offset (victim, nb);//重新定义remainder
av->top = remainder;//top更新
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
check_malloced_chunk (av, victim, nb);//检测chunk
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);//初始化chunk
return p;//返回指针
}
/* 当我们使用原子操作来释放快速块时,我们可以得到
这里适用于所有块大小. */
else if (have_fastchunks (av))//存在fastbin
{
malloc_consolidate (av);//进行合并
/* restore original bin index */
if (in_smallbin_range (nb))
idx = smallbin_index (nb);
else
idx = largebin_index (nb);
}
/*
否则,继电器将处理系统相关的情况
*/
else
{
void *p = sysmalloc (nb, av);
if (p != NULL)
alloc_perturb (p, bytes);
return p;
}
}
}
Free
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
INTERNAL_SIZE_T size; /* its size */
mfastbinptr *fb; /* associated fastbin */
mchunkptr nextchunk; /* next contiguous chunk */
INTERNAL_SIZE_T nextsize; /* its size */
int nextinuse; /* true if nextchunk is used */
INTERNAL_SIZE_T prevsize; /* size of previous contiguous chunk */
mchunkptr bck; /* misc temp for linking */
mchunkptr fwd; /* misc temp for linking */
const char *errstr = NULL;
int locked = 0;
size = chunksize (p);//获取p指针大小
/* 不会影响性能的少量安全检查:
分配器从不在地址空间的末尾进行打包。
因此,我们可以排除一些可能出现的大小值
这里是偶然的,或者是某个入侵者的“设计”. */
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
{
errstr = "free(): invalid pointer";
errout:
if (!have_lock && locked)
(void) mutex_unlock (&av->mutex);
malloc_printerr (check_action, errstr, chunk2mem (p), av);
return;
}
/* 我们知道每个块的大小至少为MINSIZE字节或一个
MALLOC_对齐的倍数. */
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
{
errstr = "free(): invalid size";
goto errout;
}
check_inuse_chunk(av, p);
/*
如果符合条件,请将区块放在fastbin上,以便可以找到它
并在malloc中快速使用.
*/
if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
#if TRIM_FASTBINS
/*
如果设置了TRIM_FASTBINS,则不要放置块
将顶部与快速垃圾箱相连
*/
&& (chunk_at_offset(p, size) != av->top)//判断大小是否位于fastbin范围之中,并且与top_chunk不相邻
#endif
) {
if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av->system_mem, 0))
{
/* 在这一点上,我们可能没有锁和并发修改
系统的内存可能会出现假阳性。重做测试
拿到锁后. */
if (have_lock
|| ({ assert (locked == 0);
mutex_lock(&av->mutex);
locked = 1;
chunk_at_offset (p, size)->size <= 2 * SIZE_SZ
|| chunksize (chunk_at_offset (p, size)) >= av->system_mem;
}))
{
errstr = "free(): invalid next size (fast)";
goto errout;
}
if (! have_lock)
{
(void)mutex_unlock(&av->mutex);
locked = 0;
}
}
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
set_fastchunks(av);
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);
/* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */
mchunkptr old = *fb, old2;
unsigned int old_idx = ~0u;
do
{
/* Check that the top of the bin is not the record we are going to add
(i.e., double free). */
if (__builtin_expect (old == p, 0))//判断是否存在double free
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
/* 检查顶部fastbin区块的大小是否与
我们正在添加的块的大小。我们可以取消对旧版本的引用
除非我们有锁,否则它可能已经被锁上了
解除分配。有关实际检查,请参见下面的旧_IDX的使用. */
if (have_lock && old != NULL)//此时将p插入fastbin链中
old_idx = fastbin_index(chunksize(old));
p->fd = old2 = old;
}
while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);
if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
{
errstr = "invalid fastbin entry (free)";
goto errout;
}
}
/*
在其他未映射的块到达时合并它们.
*/
else if (!chunk_is_mmapped(p)) {
if (! have_lock) {
(void)mutex_lock(&av->mutex);
locked = 1;
}
nextchunk = chunk_at_offset(p, size);
/* 轻量测试:检查块是否已经是顶部块. */
if (__glibc_unlikely (p == av->top))
{
errstr = "double free or corruption (top)";
goto errout;
}
/* Or whether the next chunk is beyond the boundaries of the arena. */
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av->top + chunksize(av->top)), 0))
{
errstr = "double free or corruption (out)";
goto errout;
}
/* Or whether the block is actually not marked used. */
if (__glibc_unlikely (!prev_inuse(nextchunk)))
{
errstr = "double free or corruption (!prev)";
goto errout;
}
nextsize = chunksize(nextchunk);
if (__builtin_expect (nextchunk->size <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
{
errstr = "free(): invalid next size (normal)";
goto errout;
}
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
/* consolidate backward */
if (!prev_inuse(p)) {//如果物理前一块chunk没有被使用,则进行合并
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);//进行合并
}
if (nextchunk != av->top) {//如果下一块不为top_chunk,则进行判断是否可以向后合并
/* get and clear inuse bit */
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
/* consolidate forward */
if (!nextinuse) {//判断下一个是否使用,如果没有被使用,进行合并
unlink(av, nextchunk, bck, fwd);//机械能Unlink
size += nextsize;
} else
clear_inuse_bit_at_offset(nextchunk, 0);//清除标记位
/*
将区块放入未排序区块列表中。块是
在完成后才放入常规垃圾箱
被给予一次在malloc中使用的机会.
*/
bck = unsorted_chunks(av);//获取unsorted_bin链
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))
{
errstr = "free(): corrupted unsorted chunks";
goto errout;
}
p->fd = fwd;
p->bk = bck;
if (!in_smallbin_range(size))//判断其是否不存在于smallbin链范围之中
{
p->fd_nextsize = NULL;
p->bk_nextsize = NULL;
}
bck->fd = p;
fwd->bk = p;//重新排布链表
set_head(p, size | PREV_INUSE);
set_foot(p, size);
check_free_chunk(av, p);
}
/*
如果区块与当前高端内存相邻,
合并到顶部
*/
else {//否则合并到top_chunk中
size += nextsize;
set_head(p, size | PREV_INUSE);//设置标记位
av->top = p;//更新top_chunk
check_chunk(av, p);
}
/*
如果释放较大的空间,则可能会合并周围的
大块。然后,如果未使用的最高内存总量超过trim
阈值,要求malloc_trim减小顶部.
除非max_fast为0,否则我们不知道是否存在fastbins
接近顶部,所以我们无法确定阈值
已到达,除非合并fastbins。但是我们
不想在每个免费的基础上进行整合。作为妥协,,
如果FASTBIN\u合并\u阈值,则执行合并
到达.
*/
if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
if (have_fastchunks(av))//存在fastbin链
malloc_consolidate(av);//进行对fastbin进行合并
if (av == &main_arena) {
#ifndef MORECORE_CANNOT_TRIM
if ((unsigned long)(chunksize(av->top)) >=
(unsigned long)(mp_.trim_threshold))
systrim(mp_.top_pad, av);
#endif
} else {
/* 始终尝试heap_trim(),即使顶部块不是
较大,因为相应的堆可能会消失. */
heap_info *heap = heap_for_ptr(top(av));
assert(heap->ar_ptr == av);
heap_trim(heap, mp_.top_pad);
}
}
if (! have_lock) {
assert (locked);
(void)mutex_unlock(&av->mutex);
}
}
/*
如果区块是通过mmap分配的,则通过munmap()释放.
*/
else {
munmap_chunk (p);
}
}