在C++ STL的内存管理体系中,一级配置器(__malloc_alloc_template
)和二级配置器(__default_alloc_template
)存在以下一些区别:
1. 内存分配来源及方式
- 一级配置器:
一级配置器直接调用malloc
、realloc
、free
等C语言中的内存分配和释放函数来从操作系统堆获取和归还内存。当内存分配请求无法满足(比如malloc
失败)时,它会尝试调用一个由用户自定义的oom_malloc
函数(如果定义了的话)来处理内存不足的情况,若没有自定义的处理函数,则会抛出bad_alloc
异常来告知内存分配失败。例如,下面是一级配置器大致的allocate
函数逻辑示意(简化版):
template <int __inst>
void* __malloc_alloc_template<__inst>::allocate(size_t __n) {
void* __result = malloc(__n);
if (__result == 0) {
// 处理内存不足情况,比如调用自定义oom_malloc函数或者抛异常
if (__malloc_alloc_oom_handler) {
// 假设存在自定义的oom处理函数,调用它
(*__malloc_alloc_oom_handler)();
__result = malloc(__n);
} else {
throw bad_alloc();
}
}
return __result;
}
- 二级配置器:
二级配置器采用了内存池(Memory Pool)的技术来管理内存分配。它会预先准备好一些不同大小规格(通常按照8字节、16字节等固定大小区间划分)的空闲内存块链表,当容器等需要分配内存时,先从这些合适大小的空闲内存块链表中查找并分配,如果链表中有可用的内存块,就直接取出分配给请求方;若链表中没有合适的内存块了,才会通过调用malloc
等函数向操作系统申请一块较大的内存,然后分割成合适大小的内存块添加到相应的空闲链表中,并取出一部分分配给请求者。当内存回收时,它会把释放的内存块放回对应的空闲链表中,以便下次复用,这样可以有效减少内存碎片以及频繁向操作系统申请内存的开销。
2. 处理内存块大小的策略
-
一级配置器:
对于任何大小的内存分配请求,一级配置器都是简单地转交给malloc
等函数去处理,没有针对不同大小的内存块进行特殊的分类管理,只是单纯依赖操作系统堆的内存分配机制,每次分配请求都是独立地向堆申请对应大小的内存空间。 -
二级配置器:
二级配置器针对较小的内存块(一般小于128字节,不同实现可能略有差异)有专门的管理策略,也就是利用前面提到的那些固定大小规格的空闲内存块链表来处理。它把内存块按照一定大小区间进行分类,比如对于8字节的整数倍大小且小于128字节的内存块,分别建立不同的链表来存储相应大小的空闲内存块,这样对于频繁分配和释放小内存块的情况(比如list
容器中节点的内存管理,节点通常占用内存较小),可以高效地复用内存块,避免了小内存块频繁申请和释放造成的内存碎片以及性能损耗;而对于大于等于128字节的内存分配请求,二级配置器就直接调用malloc
去申请,相当于把这种较大内存分配的情况交给一级配置器的底层机制去处理了。
3. 内存分配效率及适用场景
-
一级配置器:
由于它直接依赖操作系统的内存分配函数,每次分配内存都要进入内核态(涉及系统调用)去操作堆内存,相对来说开销较大,尤其是在频繁进行小内存块分配和释放的场景下,效率会比较低,因为系统调用本身存在一定的性能成本。不过它能处理任意大小的内存分配请求,在分配较大内存块(比如一次性分配较大数组空间等情况)时,其功能完整性得以体现,并且在一些对内存管理要求相对简单、不太在意频繁系统调用带来的性能损耗的场景中可以满足需求。 -
二级配置器:
在处理小内存块分配时效率较高,因为它利用内存池和空闲链表机制,能快速从已有的空闲内存块中分配或者回收内存,避免了多次系统调用带来的性能开销,很适合像list
、map
等容器中频繁涉及小内存块操作(如节点内存管理等)的场景。但对于较大内存块的分配,它最终还是要借助一级配置器的底层malloc
调用,在这种情况下,就和一级配置器在效率上没有本质区别了,而且其实现相对复杂一些,需要维护内存池以及多个空闲链表等数据结构来管理内存。
4. 内存回收管理
-
一级配置器:
通过调用free
函数直接将内存归还给操作系统堆,释放的内存就完全由操作系统再次统一分配管理,不会在配置器内部做更多的内存复用等特殊处理,是一种比较简单直接的内存回收方式。 -
二级配置器:
当回收内存时,它会根据回收内存块的大小,判断是否属于其管理的小内存块范围(小于128字节等情况),如果是,就将该内存块放回对应的空闲内存块链表中,方便下次有相同大小内存需求时直接复用;只有当回收的内存块不符合其内部管理的小内存块条件(比如是较大的内存块或者配置器要销毁等情况),才会调用free
函数将内存归还给操作系统,这样使得内存资源在配置器内部能得到更充分的循环利用,减少了对操作系统堆内存频繁申请和释放的依赖。
总之,一级配置器和二级配置器在C++ STL中各司其职,通过不同的内存管理策略来满足不同场景下容器等对内存分配和回收的需求,共同协作以实现相对高效且灵活的内存管理机制。