堆内存(7)——内存释放入口函数_lib_free

_libc_free

  1. 先检查是否有钩子函数,有则调用并返回。

  2. 如果是 mmap 分配的 chunk,则用 munmap 将其释放,如果释放的 chunk 大小大于 mmap 分配的阈值,且未关闭动态调整阈值开关,则调整一下 mmap 的阈值为当前 chunk 大小。

  3. 调用 _int_free 释放内存。

  4. chunk_size < 128B ,且 chunk 不与 top chunk 相邻则放入 fast bins 中,这里不会加锁,而是用的 CAS,返回。

  5. 加锁分配区,前一个 chunk 若空闲,则合并。

  6. 后一个 chunk 若为 top chunk ,则将其合并到 top chunk 中,若不是也合并,将其放到 unosrted bin

  7. 如果合并的 chunk 大于 64KB,则开始整合 fast binsunsorted bin ,若 top chunk 的大小 大过 收缩阈值了,默认为 128K ,则收缩堆,也就是还给内核。

  8. 也就是说 释放内存回内核 需要两个条件, chunk_size > 64KB,且 top chunk 大于收缩阈值,则释放。

    preview

void
__libc_free (void *mem)
{
  mstate ar_ptr;
  mchunkptr p;                          /* chunk corresponding to mem */

  void (*hook) (void *, const void *)
    = atomic_forced_read (__free_hook);
  if (__builtin_expect (hook != NULL, 0))
    {
      (*hook)(mem, RETURN_ADDRESS (0));
      return;
    }

  if (mem == 0)                              /* free(0) has no effect */
    return;

  p = mem2chunk (mem);

  if (chunk_is_mmapped (p))                       /* release mmapped memory. */
    {
      /* See if the dynamic brk/mmap threshold needs adjusting.
         Dumped fake mmapped chunks do not affect the threshold.  */
      if (!mp_.no_dyn_threshold
          && chunksize_nomask (p) > mp_.mmap_threshold
          && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX
          && !DUMPED_MAIN_ARENA_CHUNK (p))
        {
          mp_.mmap_threshold = chunksize (p);
          mp_.trim_threshold = 2 * mp_.mmap_threshold;
          LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
                      mp_.mmap_threshold, mp_.trim_threshold);
        }
      munmap_chunk (p);
      return;
    }

  MAYBE_INIT_TCACHE ();

  ar_ptr = arena_for_chunk (p);
  _int_free (ar_ptr, p, 0);
}

__libc_free__lib_malloc一样首先查看是否有__free_hook函数,如果有就直接调用,这里假设没有默认函数可用。接下来通过mem2chunk将虚拟内存的指针mem转换为对应的chunk指针p

​ 因为一个使用中的chunk结构体只使用其prev_sizesize字段,因此这里只需要减去2*SIZE_SZ。接下来,chunk_is_mmapped用来检查size最低三位中的标志位,判断该chunk是否是由mmap分配的,如果是,就调用munmap_chunk释放该chunk并返回,在调用munmap_chunk之前,需要更新全局的mmap阀值和收缩阀值。

munmap_chunk

static void
munmap_chunk (mchunkptr p)
{
  size_t pagesize = GLRO (dl_pagesize);
  INTERNAL_SIZE_T size = chunksize (p);

  assert (chunk_is_mmapped (p));

  /* Do nothing if the chunk is a faked mmapped chunk in the dumped
     main arena.  We never free this memory.  */
  if (DUMPED_MAIN_ARENA_CHUNK (p))
    return;

  uintptr_t mem = (uintptr_t) chunk2mem (p);
  uintptr_t block = (uintptr_t) p - prev_size (p);
  size_t total_size = prev_size (p) + size;
  /* Unfortunately we have to do the compilers job by hand here.  Normally
     we would test BLOCK and TOTAL-SIZE separately for compliance with the
     page size.  But gcc does not recognize the optimization possibility
     (in the moment at least) so we combine the two values into one before
     the bit test.  */
  if (__glibc_unlikely ((block | total_size) & (pagesize - 1)) != 0
      || __glibc_unlikely (!powerof2 (mem & (pagesize - 1))))
    malloc_printerr ("munmap_chunk(): invalid pointer");

  atomic_decrement (&mp_.n_mmaps);
  atomic_add (&mp_.mmapped_mem, -total_size);

  /* If munmap failed the process virtual memory address space is in a
     bad shape.  Just leave the block hanging around, the process will
     terminate shortly anyway since not much can be done.  */
  __munmap ((char *) block, total_size);
}

​ 首先将chunk转换为size,获得前一个chunk的指针block,计算这两个chunk的size之和至total_size,接着对全局结构mp_进行相应的设置后,就通过__munmap释放这两个chunk。根据malloc的源码可知,由mmap分配的chunk是独立的,大部分情况下,p->prev_size为0,因此这里还是释放一个chunk,特殊情况下需要释放两个chunk,特殊情况请参考_int_malloc中的代码。

__munmap再往下就是系统调用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值