《STL源码分析》学习笔记 — 空间配置器 — pool_allocator


__pool_alloc 对应了书中所说的第一级配置器,其基类则对应第二级配置器。该配置器的工作目标是 减少小额区块造成的内存碎片。我们多次申请内存时,其在堆空间中的分配并不是连续的。因此,如果我们在堆中申请了许多小块的内存,就会造成当我们想要分配大内存时,系统找不到连续的内存以满足我们需要的空间。
我们这里使用的 cygwin 只提供了 gcc 的头文件。我们这里的函数实现有些是位于源文件中的。因此,我们将会使用 gcc10.2.0 的源码分析。

一、__pool_alloc_base

该基类定义了满足小额区块的内存上限(128bytes)。如果超过此上限,则交由派生类进行内存管理。当内存块大小不超过此上限时,则在内存池中进行内存管理(memory pool):

/**
*  @brief  Base class for __pool_alloc.
*
*  重要的实现性质:
*  0. 如果全局授权,则通过new分配内存
*  1. 如果用户要求的内存大于_S_max_bytes,直接从堆中分配内存
*  2. 在所有其它情况中,我们分配对象的大小为_S_round_up(requested_size)
*     这样用户持有足够的大小信息,使得我们可以将用户不再使用的对象防止到适当的 free-list中,而不会永远丢失该块内存
*/
class __pool_alloc_base
{
   
    typedef std::size_t size_t;
protected:

    enum {
    _S_align = 8 }; // 对齐方式
    enum {
    _S_max_bytes = 128 }; // 小额区块的内存上限
    enum {
    _S_free_list_size = (size_t)_S_max_bytes / (size_t)_S_align }; // 维护的区块链表数量

    union _Obj
    {
   
        union _Obj* _M_free_list_link;    // 下一个链表节点
        char        _M_client_data[1];    // 用户看到的内存地址
    };

    static _Obj* volatile         _S_free_list[_S_free_list_size]; // 自由链表数组

    // 维护内存块状态
    static char*                  _S_start_free; // 内存池中可用空间的起始地址
    static char*                  _S_end_free; // 内存池中可用空间的结束地址
    static size_t                 _S_heap_size; // 已经从堆中申请的总空间大小

    size_t _M_round_up(size_t __bytes)
    {
    return ((__bytes + (size_t)_S_align - 1) & ~((size_t)_S_align - 1)); }

    _GLIBCXX_CONST _Obj* volatile* _M_get_free_list(size_t __bytes) throw ();

    __mutex& _M_get_mutex() throw ();

    void* _M_refill(size_t __n);

    char* _M_allocate_chunk(size_t __n, int& __nobjs);
};

1、_M_get_free_list

这个接口是用户获取指定 free-list 的接口。

// Definitions for __pool_alloc_base.
__pool_alloc_base::_Obj* volatile* __pool_alloc_base::_M_get_free_list(size_t __bytes) throw ()
{
   
    size_t __i = ((__bytes + (size_t)_S_align - 1) / (size_t)_S_align - 1);
    return _S_free_list + __i;
}

这里用到了我们在头文件中声明的对齐方式属性 _S_align。不难看出,这里计算得到的 i 实际上是 bytes 除以 _S_align 后向上取整并-1。这里-1是因为 i 作为数组下标使用。那么返回的值就是 _S_free_list 的第 i+1 个元素地址。

看到这里,我们就明白 _S_free_list 中实际上保存了指向 _S_align 不同倍数大小的 free-list 指针。其第一个元素保存的是大小为0-8的那些内存块,第二个是9-16,依此类推。_S_free_list 的类型是 union _Obj*。从其定义,不难看出它实际上是维护了数个链表。因此,我们可以得出一个结论:对于 _S_align 某个整数倍范围内(如25-32)的内存,如果内存池中有相应空间可分配,那么 _S_free_list 中对应下标的链表中的首元素一定是处于未使用状态的

2、_M_round_up

此函数用于将某个数量扩展为8字节对其版本。

3、_M_allocate_chunk

// 从大的内存块(内存池)中(_S_start_free-_S_end_free)分配空间以避免碎片化。前提是充分利用空间
// __n 表示对象个数, __nobjs 表示每个对象大小(引用传递,以供派生类知道实际构造的对象个数)。
// 假设 __n 是适当对齐的,即为_S_align的整数倍。
// 这个函数要求调用者持有锁
char* __pool_alloc_base::_M_allocate_chunk(size_t __n, int& __nobjs)
{
   
    char* __result; // 返回值
    size_t __total_bytes = __n * __nobjs; // 需要的空间
    size_t __bytes_left = _S_end_free - _S_start_free; // 当前空闲块中剩余的空间

    if (__bytes_left >= __total_bytes)
    {
   
    	// 如果当前空闲块中剩余的空间,分配并更新空闲块的起始地址
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return __result ;
    }
    else if (__bytes_left >= __n)
    {
   
    	// 如果当前空闲块中剩余的空间不满足空间要求,但是至少能满足分配给一个对象
    	// 计算能分配给几个对象及需要的空间,分配并返回
        __nobjs = (int)(__bytes_left / __n);
        __total_bytes = __n * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return __result;
    }
    else
    {
   
        if (__bytes_left > 0)
        {
   
        	// 充分利用剩余的空闲块,将剩余的空间放到_S_free_list数组对应的链表中
        	// 从这里我们能看出来,这要求内存池中剩余的大小也是 _S_align 的整数倍,否则不能直接插入到某个内存池链表中
        	// 实现这个要求的方式就是:初始分配的内存时 _S_align 的整数倍,每次取对象时,其大小也是 _S_align 的整数倍
            _Obj* 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
侯捷的《STL源码剖析》是一本关于STL(标准模板库)的学习笔记。这本书深入解析了STL的实现原理和设计思路,对于理解STL的内部机制和使用方法非常有帮助。这些学习笔记记录了作者在学习侯捷的《STL标准库和泛型编程》课程时的心得和总结,对于理解STL源码和进行泛型编程都具有指导意义。 这本书涉及了STL的各个模块,包括容、迭代、算法等,并解释了它们的实现原理和使用方法。通过学习这本书,你可以更好地理解STL的底层实现和使用技巧。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [STLSourceAnalysis:stl原始码剖析(侯捷)的学习笔记](https://download.csdn.net/download/weixin_42175776/16069622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [候捷老师STL源码剖析视频课程笔记](https://blog.csdn.net/weixin_46065476/article/details/125547869)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [侯捷——STL源码剖析 笔记](https://blog.csdn.net/weixin_45067603/article/details/122770539)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值