SGISTL源码探究-第一级配置器

本文详细解析了第一级配置器的实现原理,包括如何处理大于128bytes的大内存分配情况,通过malloc和free进行内存的申请与释放,并介绍了oom处理机制及用户自定义内存不足时的处理函数。

前言

在本小节中,我们主要介绍第一级配置器的做法。

第一级配置器(__malloc_alloc_template)

引入

第一级配置器用于处理大于128bytes的情况,即申请大的内存。直接使用mallocfree申请释放即可。(现在有可能不是以128bytes为标准了,不过源码本来就是主着学习的目的,不管现在是多少,至少你能从中学到思想就够了)

深入源码
  //这里是一个模板偏特化技术,即int类型,可以提供不同的实例,比如__malloc_alloc_template<0>和__malloc_alloc_template<1>就是两个不同的版本。这样做的好处是拥有不同的static。
  template <int inst>
  class __malloc_alloc_template
  {
    private:
    //oom代表out of memory
    //以下函数用于处理内存不足的情况
    static void *oom_malloc(size_t);
    static void *oom_realloc(void *, size_t);
    static void (*__malloc_alloc_oom_handler)();
    public:
    //用malloc申请n个大小的空间
    static void *allocate(size_t n)
    {
      void *result = malloc(n);
      //如果失败了,则调用处理函数
      if(0 == result) result = oom_malloc(n);
      return result;
    }
    //用free释放空间
    static void deallocate(void *p, size_t )
    {
      free(p);
    }
    //用realloc重新申请空间
    static void *reallocate(void *p, size_t, size_t new_sz)
    {
      void *result = realloc(p, new_sz);
      //如果失败了,则调用处理函数
      if(0 == result) result = oom_realloc(p, new_sz);
      return result;
    }

    //这里实际上是模仿了操作符new里面的`set_new_handler()`函数,可以让用户自己定义内存不足时的处理函数,并返回旧的处理方法。(这里的参数还有样子可以看起来有点绕,其实就是两个函数指针,返回的类型是函数指针,传入的参数也是函数指针)
    static void (* set_malloc_handler)(void (*f)())()
    {
      void (*old)() = __malloc_alloc_oom_handler;
      __malloc_alloc_oom_handler = f;
      return (old);
    }
  };

接下来就是oom部分

  template <int inst>
  void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;

  //当malloc申请内存不足时
  template <int inst>
  void *__malloc_alloc_template<inst>::oom_malloc(size_t n)
  {
    //引入局部函数指针
    void (*my_malloc_handler)();
    void *result;

    //注意,这是一个死循环,要么申请内存成功,要么抛出异常。否则一直执行出错处理程序。(这和new里面的机制是一样的)
    for(;;)
    {
      my_malloc_handler = __malloc_alloc_oom_handler;
      //如果__malloc_alloc_oom_handler为null的话,那么直接抛出异常
      if(0 == my_malloc_handler)
      {
        __THROW_BAD_ALLOC;
      }
      //否则调用处理程序
      (*my_malloc_handler)();
      //尝试分配,如果失败,重新循环;如果成功了,退出
      result = malloc(n);
      if(result) return (result);
    }
  }

  //oom_realloc和oom_malloc的处理逻辑是相同的
  template <int inst>
  void *__malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
  {
    void (*my_malloc_handler)();
    void *result;

    for(;;)
    {
      my_malloc_handler = __malloc_alloc_oom_handler;
      if(0 == my_malloc_handler)
      {
        __THROW_BAD_ALLOC;
      }
      (*my_malloc_handler)();
      result = realloc(p, n);
      if(result) return (result);
    }
  }

  typedef __malloc_alloc_template<0> malloc_alloc;
分析
  1. 在第一级配置器中,当mallocrealloc分配内存不成功时,会调用各自的oom_xxx,里面会调用用户自己设置的__malloc_alloc_oom_handler,如果成功申请,则返回,否则当用户没有设置__malloc_alloc_oom_handler时,就直接抛出异常了,如果设置了,则调用处理程序,然后又如此反复。该机制和new的确类似,如果你已经了解new的做法,那么这个就很容易理解了。
  2. 至于那个template <int inst>,如果将其直接实现为类,假设有多个实例,会有点问题。我们都知道static变量在类中只会被保留一份,如果将__malloc_alloc_template实现成简单的类,那么每次实例化对象的时候,__malloc_alloc_oom_handler都是上一次改变的值,而我们要求的是,应该由用户自己来定义处理程序,所以应该每次使用一个新的一级配置器时,处理程序的初始值都该为null。(你可能会想,那就不用static关键字,别忘了static还有隐藏变量和函数的作用,用于封装)。不过,事实上我们根本不用担心这么多,因为最后一行代码直接将__malloc_alloc_template<0>typedef成了malloc_alloc,只用了一个一级配置器。

小结

在本小节中,主要介绍了第一级配置器中申请以及释放内存的做法,申请内存时类似new操作符的机制。使用第一级配置器还是第二级配置器主要是根据要求分配的空间大小来定。在下一小节我们主要分析第二级配置器。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值