STL源码剖析-空间配置器

1.allocator是空间配置器而不是内存配置器,空间不一定是内存,也可以是磁盘或其他辅助存储介质。但SGI STL提供的配置器配置的对象是内存。

2.SGI标准的空间配置器,std::alloctor
  
SGI定义了一个符合部分标准,名为alloctor的配置器,效率不高,只把c++的::operator new 和 ::operator delete做了一层薄薄的封装。

3.c++用new构造一个对象时,包含两阶段操作:(1)调用::operator new 配置内存;(2)调用该对象的构造函数构造对象内容。 delete销毁一个对象时,也包含两阶段操作:(1)调用该对象的析构函数将对象析构;(2)调用::operator delete释放内存。

STL allocator将上述两阶段操作区分开来。内存配置由alloc::allocator()负责,内存释放操作由alloc::deallocator()负责;对象构造由::constructor()负责,对象析构由::destroy负责。

这里写图片描述

4.构造和析构基本工具

对象的构造:

    template <class T1, class T2>  
    inline void construct(T1* p, const T2& value) {  
      new (p) T1(value);  
    }  

上述函数接受一个指针p和一个初值value,用定位new运算子将初值设定到指针所指的空间上。

对象的析构:

template <class T>  //第一版本  
inline void destroy(T* pointer) {  
    pointer->~T();  
}  

template <class ForwardIterator>  //第二版本  
inline void destroy(ForwardIterator first, ForwardIterator last) {  
  __destroy(first, last, value_type(first));  
}  
template <class ForwardIterator>  
inline void  
__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {  
  for ( ; first < last; ++first)  
    destroy(&*first);  
}  
template <class ForwardIterator>   
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {} 

上述destroy()的第一版本接受一个指针,将指针所指的对象析构掉。第二版本接受first和last两个迭代器,将两个迭代器范围内的对象析构掉。在第二版本中运用了traits编程技法,traits会得到当前对象的一些特性,再根据特性的不同分别对不同特性的对象调用相应的方法。在第二版本中,STL会分析迭代器所指对象的has_trivial_destructor特性的类型(只有两种:__true_type和__false_type),如果是__true_type,STL就什么第一不做;如果是false_type,就会调用每个对象的析构函数来销毁这组对象。

5.内存的配置

SGI设计了双层配置器,第一级配置器直接使用malloc()和free(),第二级配置器采取以下策略:当配置区块超过128bytes时,调用第一级配置器;当配置区块小于128bytes时,采用内存池方式。

这里写图片描述

6.第一级配置器

    static void * allocate(size_t n)  
    {  
        void *result = malloc(n);   //直接使用malloc()  
        if (0 == result) result = oom_malloc(n);  
        return result;  
    }  

    static void deallocate(void *p, size_t /* n */)  
    {  
        free(p);    //直接使用free()  
    }  

    static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)  
    {  
        void * result = realloc(p, new_sz);     //直接使用realloc()  
        if (0 == result) result = oom_realloc(p, new_sz);  
        return result;  
    }  

当alloc()和realloc()申请不到内存时,会调用oom_malloc()和oom_reaalloc(),这两个函数不断调用“内存不足处理函数”,直到获得足够的内存为止。如果用户没有传递“内存不足处理函数”,会抛出__THROW_BAD_ALLOC异常。

7.第二级配置器

第二级配置器的做法:如果申请内存大于128bytes时,就用第一级配置器。小于128bytes时,以内存池管理:每次配置一大块内存,并维护相应的自由链表。

具体分配过程:

allocate()函数处理过程:

1)如果申请内存大于128bytes,就调用第一级配置器,否则说明申请内存小于128bytes,转到2)

2)根据申请内存的大小n在16个free lists中找到其对应的my_free_list

3)如果对应的my_free_list中没有空闲区块,分配器首先将申请的内存大小上调至8的倍数n,调用refill(),准备重新填充my_free_list

4)否则说明有可用的空闲区块,更新my_free_list并将第一块空闲区块返回

refill()函数的处理过程:

1)调用chunk_alloc()函数申请20*n的内存空间(不一定取得到)

2)如果只获得大小为n的区块,这个区块就分配给调用者,否则从获得的区块中取出一块分配给调用者,其余的用my_free_list串接起来

chunk_alloc()函数处理过程:

1)如果内存池剩余空间大于或等于20*n的内存空间,则从这个空间中取出n*20大小的内存空间,更新start_free并返回申请到的内存空间的其实地址,否则转到2)

2)如果内存池剩余空间足够分配一个及以上的区块,则分配整数倍于n的内存空间,更新start_free,由nobjs返回实际分配到的区块个数,并返回申请到的内存空间的其实地址,否则转到3)

3)内存池中无法提供一个大小为n的区块,此时如果内存池中还有一些残余内存(这些内存大小小于n),则将这些内存插入到其对应大小的空闲分区链中

4)调用malloc向运行时库申请大小为(2*20*n+附加量)的内存空间,如果申请成功,更新start_free,end_free 和 heap_size,并递归调用chunk_alloc(),修正bobjs,否则转到5)

5)4)中调用malloc失败,此时分配器依次遍历区块足够大的free_lists,只要有一个未用区块,就释放该区块,递归调用chunk_alloc(),修正nobjs

6)如果出现意外,到处都没有内存可用了,则调用第一级配置器,看out-of-memory机制能否尽点力

对应第二级配置器的allocate()函数处理过程的第4步:

这里写图片描述

对应第二级配置器的内存池操作:

这里写图片描述

8.内存的释放

具体内存释放过程:
先判断要释放的背刺区块的大小,大于128bytes就调用第一级配置器释放内存,否则要释放的内存区块小于128bytes,就找出相应的free_list,将区块回收。

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值