STL内存分配

#### STL内存分配


由于小型的内存区块会因此内存分配碎片,SGI STL设计中使用了**双层级配置器**,**第一级配置器直接使用malloc()和free()**,申请内存大于128字节时调用;第二级配置器采用**内存池**的方式来管理。


STL内存分配器在文件`<stl_alloc.h>`中实现,**__malloc_alloc_template**和**__default_alloc_template**,分配器都需要实现两个接口allocate和deallocate。本文主要讨论第二级的分配器的实现。


**__default_alloc_template**:STL默认小内存分配器


#####分配器的实现


维护了16个free list,每个list上集合着大小分别为8,16,24,...128大小的内存块。如果请求的内存块大小大于128bytes,就转调用第一级配置器。使用了链表结构管理内存块:


```cpp
union obj{  
        union obj* free_list_link;  //当作为自由链表的一个结点时,存储其下一个节点的地址  
        char client_date[1];        //当其作为返回值时,返回的正好是分配内存的首地址  
    }  
```


相当于每个块前面4个字节用来存储节点头结构,结构体中client_data返回的实际上可用的内存块地址。free_list中找到合适的块后,会将下一个空闲块的地址写在原来这个free_list数组元素的位置。一般而言内存块是连续的。


![stl_default_allocator_free_list](https://images2015.cnblogs.com/blog/364303/201608/364303-20160831093958074-1199118235.jpg)






内存申请流程:


1. 大于128字节,使用第一级配置器向系统申请,malloc不成功会调用**set_new_handler**来处理异常。
2. 小于128字节,使用第二级适配器。如果有对应大小的free_list内存块,正常返回;如果free_list没有:

    -  2.1 向内存池要20个块大小的内存,内存池尽量满足;如果内存池也没有足够的内存:
    -  2.2 调用malloc从系统heap中申请内存,申请大小 **bytes_to_get=2*total_bytes+ROUND_UP(heap_size>>4)**,如果申请成功了并且内存池中有剩余内存,会将内存池的剩余内存非配给free_list。
3. 如果2.2失败,会求助于第一级配置器,主要是想借助于第一级配置器里的异常处理,例如**out_of_memory**中申请到一点内存。如果这一步也失败,那么就是彻底失败了。


##### 释放器


STL中调用deallocate后,对于小块内存,会放入到free_list中而不是释放回系统。
### ### C++ STL 内存分配器类详解 C++ STL 中的内存分配器(Allocator)是容器用于管理内存分配和释放的核心组件。标准库提供了默认的 `std::allocator`,但用户可以通过自定义分配器来控制容器的内存行为,例如实现内存池、共享内存或调试用的内存分配跟踪等机制[^1]。 `std::allocator` 提供了统一的接口方法,包括 `allocate` 和 `deallocate` 用于申请和释放内存,以及 `construct` 和 `destroy` 用于构造和析构对象。STL 容器如 `std::vector` 或 `std::list` 在内部使用这些方法进行元素的动态存储管理。 ```cpp template <typename T> class MyAllocator { public: using value_type = T; MyAllocator() noexcept = default; template <typename U> MyAllocator(const MyAllocator<U>&) noexcept {} T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); } void deallocate(T* p, std::size_t) noexcept { ::operator delete(p); } }; ``` 上述示例展示了如何定义一个简单的自定义分配器,并可用于替代 `std::allocator`。 ### ### 类型萃取(Traits)机制详解 类型萃取是一种泛型编程技术,广泛应用于 C++ STL 中,用于提取模板参数类型的特性信息。通过模板偏特化,萃取器可以为不同类型提供不同的行为,从而在编译期根据类型信息做出决策。 萃取器通常被封装在一个结构体中,该结构体包含一组静态常量、嵌套类型定义或静态函数,用于描述目标类型的属性。例如,在迭代器库中,`std::iterator_traits` 被用来获取迭代器所指向元素的类型信息,如 `value_type`、`difference_type` 等。 ```cpp template <typename Iterator> struct iterator_traits { using value_type = typename Iterator::value_type; using pointer = typename Iterator::pointer; using reference = typename Iterator::reference; using difference_type = typename Iterator::difference_type; using iterator_category = typename Iterator::iterator_category; }; ``` 对于原生指针,还可以通过偏特化版本专门处理: ```cpp template <typename T> struct iterator_traits<T*> { using value_type = T; using pointer = T*; using reference = T&; using difference_type = ptrdiff_t; using iterator_category = std::random_access_iterator_tag; }; ``` 萃取器不仅限于迭代器,还广泛用于其他场景,例如 `std::char_traits` 用于字符串操作、`std::numeric_limits` 用于数值类型的边界信息等。这种机制使得 STL 能够以统一的方式处理各种类型,并支持高度可扩展的设计模式[^2]。 ### ### 内存分配器与类型萃取的结合应用 在实际的 STL 实现中,内存分配器和类型萃取常常协同工作。例如,容器在创建时会使用分配器来分配内存空间,并通过萃取器获取元素类型的信息以确保正确构造和析构对象。 当容器需要扩展容量时,它会调用分配器的 `allocate` 方法申请新的内存块,并利用萃取器获取元素的类型信息来调用对应的构造函数完成初始化。同样地,在销毁容器时,分配器负责释放内存,而萃取器则帮助确定如何正确调用析构函数。 此外,萃取器还能帮助优化分配策略。例如,某些高性能容器可能根据萃取到的类型大小、对齐要求或是否为 POD(Plain Old Data)类型来决定是否采用特定的内存管理策略。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值