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);
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值