SGI-STL 空间配置器深度源码剖析:内存管理的思考

49 阅读45分钟

SGI-STL 空间配置器深度源码剖析

引言:空间配置器的概述

SGI-STL 的源码我早年便已研读,该版本源代码可读性极佳,特此记录笔记以便后续查阅,接下来深入解析其设计实现的核心技巧。

从 STL 使用角度而言,空间配置器是最无需关注的组件,它始终隐藏在容器背后默默工作;但从 STL 实现角度而言,空间配置器是第一个需要理解的核心组件 ——STL 的所有数据都存储在容器中,而容器必须配置空间来存储数据;因此理解容器实现的前提,是掌握空间配置器的核心原理。

下文将从源码层面深度剖析 SGI-STL 空间配置器的实现机制。

空间配置器的核心设计:分离内存管理与对象生命周期

STL 空间配置器负责容器的内存分配 / 释放、对象构造 / 析构,直接决定 STL 的性能与可靠性。SGI-STL 空间配置器的核心设计原则:将内存分配 / 释放与对象构造 / 析构彻底分离

其定义在<memory>文件中,SGI<memory>包含两个核心头文件,分离原则正体现在二者的分工上:

  • stl_alloc.h:专注原始内存的分配与释放(字节级操作);
  • stl_construct.h:负责对象的构造与析构(类型相关操作)。

image.png

stl_construct.h:对象生命周期的控制器

stl_construct.h实现对象构造与析构的核心逻辑,通过两个核心函数完成类型感知操作:构造函数_Construct()、析构函数_Destroy()

image.png

构造函数:placement-new 的封装

用于在指定内存地址上构造对象,本质是对placement new的封装,是 C++ 分离内存分配与对象构造的核心技术。

// 带参构造:在已分配内存上调用拷贝构造函数
template <class _T1, class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
    new ((void*) __p) _T1(__value); // placement-new:仅调用构造函数,不分配内存
}

// 无参构造:在已分配内存上调用默认构造函数
template <class _T1>
inline void _Construct(_T1* __p) {
    new ((void*) __p) _T1();
}
placement-new技术关键

placement new是 C++ 特殊的 new 运算符用法,允许在预先分配的内存地址上构造对象,无需 new 自动分配新内存,彻底分离内存分配与对象初始化。语法如下:

#include <new>  // 必须包含该头文件
// 在指定地址p上构造T类型对象,参数为T的构造函数参数
new (p) T(构造函数参数);

其中p是指向已分配内存的指针(需保证内存大小、对齐方式符合 T 类型要求);调用后,p指向的原始内存会通过 T 的构造函数初始化为有效对象。

为容器内存预分配(如vector::reserve)提供底层支持,配合内存池优化可避免频繁new/delete产生的内存碎片,减少分配开销。

注意:通过placement new构造的对象,必须手动调用obj->~T()析构,再根据内存的分配方式(如 malloc/new[]),用对应的方式(free/delete[])释放底层内存,否则会导致内存泄漏。

析构函数:显式调用析构函数

仅调用对象析构函数销毁对象,不释放底层内存(内存释放由空间配置器负责)。实现上区分单个对象和范围对象销毁,并针对trivial析构函数做编译期优化。

// 单个对象析构
template <class _Tp>
inline void _Destroy(_Tp* __pointer) {
    __pointer->~_Tp();  // 显式调用析构函数,无内存释放操作
}

// 范围析构:迭代器指定区间的对象销毁
template <class _ForwardIterator>
inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
    __destroy(__first, __last, __VALUE_TYPE(__first));
}
__VALUE_TYPE 的底层实现与作用

__VALUE_TYPE 是 SGI-STL 中迭代器特性萃取的宏封装,核心作用是在编译期精准萃取出迭代器指向的元素类型(value_type) ,为容器元素的析构流程优化提供关键类型依据。

该宏定义于 stl_iterator_base.h,底层依托 iterator_traits 迭代器特性萃取器与 __value_type 辅助函数实现,是 SGI-STL 泛型编程与性能优化的核心组件之一,完整底层源码如下:

// 通用版本:迭代器特性萃取机
template <class _Iterator>
struct iterator_traits {
  typedef typename _Iterator::iterator_category iterator_category; // 迭代器类型
  typedef typename _Iterator::value_type        value_type;        // 迭代器指向元素类型
  typedef typename _Iterator::difference_type   difference_type;   // 迭代器距离类型
  typedef typename _Iterator::pointer           pointer;           // 迭代器指针类型
  typedef typename _Iterator::reference         reference;         // 迭代器引用类型
};

// 偏特化版本:针对普通指针类型
template <class _Tp>
struct iterator_traits<_Tp*> {
  typedef random_access_iterator_tag iterator_category; // 指针属于随机访问迭代器
  typedef _Tp                         value_type;        // 元素类型
  typedef ptrdiff_t                   difference_type;   // 距离类型
  typedef _Tp*                        pointer;           // 指针类型
  typedef _Tp&                        reference;         // 引用类型
};

// 偏特化版本:针对const指针类型
template <class _Tp>
struct iterator_traits<const _Tp*> {
  typedef random_access_iterator_tag iterator_category; // const指针也属于随机访问迭代器
  typedef _Tp                         value_type;        // 元素类型保持不变
  typedef ptrdiff_t                   difference_type;   // 距离类型
  typedef const _Tp*                  pointer;           // const指针类型
  typedef const _Tp&                  reference;         // const引用类型
};

// 重载函数iterator_category、distance_type和value_type不属于C++标准
// (它们已被iterator_traits结构体取代)
// 包含它们是为了向后兼容HP STL
// 我们为这些函数引入内部名称

// 内部函数:获取迭代器类型标签
template <class _Iter>
inline typename iterator_traits<_Iter>::iterator_category
__iterator_category(const _Iter&)
{
  typedef typename iterator_traits<_Iter>::iterator_category _Category;
  return _Category();
}

// 内部函数:获取迭代器距离类型
template <class _Iter>
inline typename iterator_traits<_Iter>::difference_type*
__distance_type(const _Iter&)
{
  return static_cast<typename iterator_traits<_Iter>::difference_type*>(0);
}

// 内部函数:获取迭代器值类型
template <class _Iter>
inline typename iterator_traits<_Iter>::value_type*
__value_type(const _Iter&)
{
  return static_cast<typename iterator_traits<_Iter>::value_type*>(0);
}

// 对外接口:获取迭代器类型标签
template <class _Iter>
inline typename iterator_traits<_Iter>::iterator_category
iterator_category(const _Iter& __i) { return __iterator_category(__i); }

// 对外接口:获取迭代器距离类型
template <class _Iter>
inline typename iterator_traits<_Iter>::difference_type*
distance_type(const _Iter& __i) { return __distance_type(__i); }

// 对外接口:获取迭代器值类型
template <class _Iter>
inline typename iterator_traits<_Iter>::value_type*
value_type(const _Iter& __i) { return __value_type(__i); }

#define __ITERATOR_CATEGORY(__i) __iterator_category(__i) // 宏:获取迭代器类型标签
#define __DISTANCE_TYPE(__i)     __distance_type(__i)     // 宏:获取迭代器距离类型
#define __VALUE_TYPE(__i)        __value_type(__i)        // 宏:获取迭代器值类型
编译期类型萃取逻辑

__VALUE_TYPE 依托 iterator_traits 模板的通用版 + 指针偏特化版,在编译期完成迭代器 value_type 的无差别推导,完美兼容两类迭代器:

  • 自定义迭代器(如 vector<int>::iterator):直接读取迭代器内部嵌套定义的 value_type 成员;
  • 原生指针 /const 指针(如 int*/const int*):通过指针偏特化版本自动推导,const 指针的 value_type 会剔除 const 修饰,保留元素原始基础类型(如 const int*value_typeint)。 整个萃取过程仅发生在编译期,核心目标是获取迭代器指向的元素类型信息,而非返回运行时可访问的有效指针。
底层实现本质

__VALUE_TYPE 底层调用 __value_type 辅助函数,其核心实现为:

return static_cast<typename iterator_traits<_Iter>::value_type*>(0);

该操作的本质是将空指针常量 0 强制转换为萃取得到的 value_type 指针类型,返回值是值为 0 的类型指针空值仅传递类型信息,无运行时值

  • 无任何内存分配、无计算逻辑、无运行时操作;
  • 仅通过指针的类型信息传递编译期萃取的 value_type,作为编译期重载决议的「类型标签」;
  • 最终会被编译器完全优化,不产生任何可执行代码,实现零运行时开销。
析构流程的性能优化

__VALUE_TYPE 萃取的类型信息,是 SGI-STL 迭代器区间对象销毁的核心依据,通过编译期类型标签分派__destroy 析构辅助函数深度联动,实现析构流程的极致性能优化:

  • __destroy 接收 __VALUE_TYPE 传递的元素类型指针标签,结合 __type_traits 类型萃取器,在编译期判断该类型是否拥有 trivial 析构函数(编译器自动生成、无自定义资源清理逻辑的空析构函数,如 int/char 等基础数据类型);
  • 若为 trivial 析构函数(标记为 __true_type):直接跳过析构循环,无任何遍历与调用开销,最大化提升性能;
  • 若为非 trivial 析构函数(标记为 __false_type)(如 string/vector 等需手动释放资源的自定义类型):遍历迭代器区间,逐个调用对象析构函数,保证资源安全释放。
// 核心分派:根据类型析构函数特性,选择对应的销毁策略
template <class _ForwardIterator, class _Tp>
inline void __destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*) {
    // 萃取类型是否拥有trivial析构函数的标记
    typedef typename __type_traits<_Tp>::has_trivial_destructor _Trivial_destructor;
    // 根据标记分派到具体的销毁函数
    __destroy_aux(__first, __last, _Trivial_destructor());
}

// 非trivial析构:需逐个调用元素的析构函数
template <class _ForwardIterator>
void __destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type) {
    for ( ; __first != __last; ++__first)
        destroy(&*__first);
}

// trivial析构:无任何资源需要清理,空函数直接返回
template <class _ForwardIterator> 
inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}

为进一步优化基础类型的销毁效率,SGI-STL 还为所有基础数据类型提供了特化的空实现 _Destroy,直接跳过所有逻辑(鉴于基础类型无析构函数,无需处理):

// 基础类型特化:无析构函数,直接空实现
inline void _Destroy(char*, char*) {}
inline void _Destroy(int*, int*) {}
inline void _Destroy(long*, long*) {}
inline void _Destroy(float*, float*) {}
inline void _Destroy(double*, double*) {}
#ifdef __STL_HAS_WCHAR_T
inline void _Destroy(wchar_t*, wchar_t*) {}
#endif /* __STL_HAS_WCHAR_T */
__type_traits的底层实现与作用

__type_traits 是 SGI-STL 实现编译期类型特性判断的核心机制,作用是在编译期提取类型的关键属性(如是否拥有 trivial 构造 / 析构函数),并基于这些属性实现编译期函数重载条件编译分支,最终达成性能优化与类型安全的双重目标。

该机制定义于 type_traits.h 头文件,是现代 C++ 标准 std::type_traits 的设计雏形与基础,其具体源码如下,便不做截断展示了:

/*
 * 版权所有 (c) 1997
 * 硅图形计算机系统股份有限公司
 *
 * 特此免费授予任何人士使用、复制、修改、分发和销售本软件
 * 及其文档用于任何目的的权限,前提是上述版权声明出现在所有副本中,
 * 并且该版权声明和本许可声明出现在支持文档中。硅图形公司不对
 * 本软件是否适用于任何目的作出任何陈述。
 * 它按"原样"提供,不提供任何明示或暗示的担保。
 */

#ifndef __TYPE_TRAITS_H
#define __TYPE_TRAITS_H

#ifndef __STL_CONFIG_H
#include <stl_config.h>   // 包含STL配置头文件
#endif

/*
本头文件提供了一个基于类型属性进行编译期分发的框架。
这在编写模板代码时非常有用。
例如,在复制一个未知类型的数组时,
了解该类型是否拥有简单拷贝构造函数会很有帮助,
以此决定是否可以使用memcpy。

类模板__type_traits提供了一系列typedef,
每个类型要么是__true_type,要么是__false_type。
__type_traits的参数可以是任意类型。
该模板中的typedef将通过以下方式获得正确的值:
    1. 通用实例化包含适用于所有类型的保守值。
    2. 可以声明特化版本来区分不同类型。
    3. 某些编译器(如Silicon Graphics的N32和N64编译器)
       将自动为所有类型提供适当的特化。

示例:

// 复制拥有非简单拷贝构造函数的元素数组
template <class T> void copy(T* source, T* destination, int n, __false_type);
// 复制拥有简单拷贝构造函数的元素数组,使用memcpy
template <class T> void copy(T* source, T* destination, int n, __true_type);

// 通过最高效的复制机制复制任意类型的数组
template <class T> inline void copy(T* source,T* destination,int n) {
   copy(source, destination, n,
        typename __type_traits<T>::has_trivial_copy_constructor());
}
*/

// 代表"真"类型标签(编译期标记)
struct __true_type {
};

// 代表"假"类型标签(编译期标记)
struct __false_type {
};

// 通用版本:类型特性萃取模板
template <class _Tp>
struct __type_traits { 
   typedef __true_type     this_dummy_member_must_be_first;
                   /* 请勿移除此成员。
                   它告知自动特化__type_traits的编译器,
                   该__type_traits模板是特殊的。
                   确保如果其他实现使用名为__type_traits的模板时程序正常工作。 */

   /* 为了支持自动生成该类特化版本的编译器,
      应遵守以下限制:
          - 你可以按需重新排序下方成员
          - 你可以移除下方任意成员
          - 不得重命名成员,除非在编译器中做对应修改
          - 新增成员将被视为普通成员,
            除非你在编译器中添加相应支持 */

   typedef __false_type    has_trivial_default_constructor;   // 是否有简单默认构造函数
   typedef __false_type    has_trivial_copy_constructor;      // 是否有简单拷贝构造函数
   typedef __false_type    has_trivial_assignment_operator;    // 是否有简单赋值运算符
   typedef __false_type    has_trivial_destructor;             // 是否有简单析构函数
   typedef __false_type    is_POD_type;                       // 是否为POD类型
};


// 提供一些特化版本。
// 对内置__type_traits支持的编译器无害,
// 对无内置支持的编译器必不可少。

#ifndef __STL_NO_BOOL

// bool类型的特化
__STL_TEMPLATE_NULL struct __type_traits<bool> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#endif /* __STL_NO_BOOL */

// char类型的特化
__STL_TEMPLATE_NULL struct __type_traits<char> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// signed char类型的特化
__STL_TEMPLATE_NULL struct __type_traits<signed char> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// unsigned char类型的特化
__STL_TEMPLATE_NULL struct __type_traits<unsigned char> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#ifdef __STL_HAS_WCHAR_T

// wchar_t类型的特化
__STL_TEMPLATE_NULL struct __type_traits<wchar_t> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#endif /* __STL_HAS_WCHAR_T */

// short类型的特化
__STL_TEMPLATE_NULL struct __type_traits<short> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// unsigned short类型的特化
__STL_TEMPLATE_NULL struct __type_traits<unsigned short> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// int类型的特化
__STL_TEMPLATE_NULL struct __type_traits<int> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// unsigned int类型的特化
__STL_TEMPLATE_NULL struct __type_traits<unsigned int> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// long类型的特化
__STL_TEMPLATE_NULL struct __type_traits<long> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// unsigned long类型的特化
__STL_TEMPLATE_NULL struct __type_traits<unsigned long> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#ifdef __STL_LONG_LONG

// long long类型的特化
__STL_TEMPLATE_NULL struct __type_traits<long long> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// unsigned long long类型的特化
__STL_TEMPLATE_NULL struct __type_traits<unsigned long long> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#endif /* __STL_LONG_LONG */

// float类型的特化
__STL_TEMPLATE_NULL struct __type_traits<float> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// double类型的特化
__STL_TEMPLATE_NULL struct __type_traits<double> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// long double类型的特化
__STL_TEMPLATE_NULL struct __type_traits<long double> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION

// 指针类型的偏特化版本(任意类型指针)
template <class _Tp>
struct __type_traits<_Tp*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */

// char* 类型特化
__STL_TEMPLATE_NULL struct __type_traits<char*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// signed char* 类型特化
__STL_TEMPLATE_NULL struct __type_traits<signed char*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// unsigned char* 类型特化
__STL_TEMPLATE_NULL struct __type_traits<unsigned char*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// const char* 类型特化
__STL_TEMPLATE_NULL struct __type_traits<const char*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// const signed char* 类型特化
__STL_TEMPLATE_NULL struct __type_traits<const signed char*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

// const unsigned char* 类型特化
__STL_TEMPLATE_NULL struct __type_traits<const unsigned char*> {
   typedef __true_type    has_trivial_default_constructor;
   typedef __true_type    has_trivial_copy_constructor;
   typedef __true_type    has_trivial_assignment_operator;
   typedef __true_type    has_trivial_destructor;
   typedef __true_type    is_POD_type;
};

#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */


// 以下功能可以用numeric_limits实现。
// 单独实现是为了减少依赖。

// 判断是否为整数类型的模板(通用版:非整数)
template <class _Tp> struct _Is_integer {
      typedef __false_type _Integral;
};

#ifndef __STL_NO_BOOL

// bool是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<bool> {
  typedef __true_type _Integral;
};

#endif /* __STL_NO_BOOL */

// char是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<char> {
  typedef __true_type _Integral;
};

// signed char是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<signed char> {
  typedef __true_type _Integral;
};

// unsigned char是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<unsigned char> {
  typedef __true_type _Integral;
};

#ifdef __STL_HAS_WCHAR_T

// wchar_t是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<wchar_t> {
  typedef __true_type _Integral;
};

#endif /* __STL_HAS_WCHAR_T */

// short是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<short> {
  typedef __true_type _Integral;
};

// unsigned short是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<unsigned short> {
  typedef __true_type _Integral;
};

// int是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<int> {
  typedef __true_type _Integral;
};

// unsigned int是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<unsigned int> {
  typedef __true_type _Integral;
};

// long是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<long> {
  typedef __true_type _Integral;
};

// unsigned long是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<unsigned long> {
  typedef __true_type _Integral;
};

#ifdef __STL_LONG_LONG

// long long是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<long long> {
  typedef __true_type _Integral;
};

// unsigned long long是整数类型
__STL_TEMPLATE_NULL struct _Is_integer<unsigned long long> {
  typedef __true_type _Integral;
};

#endif /* __STL_LONG_LONG */

#endif /* __TYPE_TRAITS_H */

// 本地变量:
// 模式:C++
// 结束:
编译期布尔标记

__type_traits 依赖两个空结构体实现编译期逻辑判断,无任何运行时开销:

struct __true_type {};  // 编译期"真"标记
struct __false_type {}; // 编译期"假"标记

__true_type__false_type 本身无任何成员变量或成员函数,仅作为编译期布尔值的 “类型标记”,通过 “类型本身” 传递语义,即它是“语义标记” 而非 “数据容器”,以此实现编译期类型的分支选择。

在内存上,C++ 标准明确规定任何对象(包括空结构体的对象)的大小都不能为 0,因此它会占用至少1字节,仅作地址占位,不占用额外有效内存,确保每个对象都有独立唯一的内存地址。

__type_traits 字段解析

__type_traits,是整个类型特性系统的核心模板,为任意类型 _Tp 定义了 6 个核心属性,默认值均为 __false_type,但会通过特化版本为基础类型(如 intchar 等)赋予正确值。其字段解析如下:

  • this_dummy_member_must_be_first:特殊标记成员,不表示任何类型特性,用于告知编译器该__type_traitsSTL 内部特殊模板,以区分用户自定义的同名模板,确保编译器自动特化时正确识别。
  • has_trivial_default_constructor:标识类型是否有 trivial 默认构造函数trivial 默认构造函数由编译器自动生成,无自定义逻辑,仅完成成员默认初始化(如 intchar 等基础类型;若自定义类显式定义默认构造函数则为非 trivial,即该属性为 __false_type)。
  • has_trivial_copy_constructor:标识类型是否有 trivial 拷贝构造函数trivial 拷贝构造函数由编译器自动生成,仅对成员变量逐位浅拷贝,无自定义逻辑(如数组、基础类型;若类含指针且自定义拷贝构造函数实现深拷贝则为非 trivial,即__false_type)。
  • has_trivial_assignment_operator:标识类型是否有 trivial 赋值运算符trivial 赋值运算符由编译器自动生成,仅对成员变量逐位赋值,无自定义逻辑(与拷贝构造函数类似,若自定义赋值运算符处理资源管理则为非 trivial,即__false_type)。
  • has_trivial_destructor:标识类型是否有 trivial 析构函数。由编译器自动生成,无自定义清理逻辑(不释放动态内存、不调用其他对象析构函数等,如 int、指针;若类包含动态内存且自定义析构函数释放资源则为非 trivial,即__false_type)。
  • is_POD_type:标识类型是否为 POD 类型(Plain Old Data,简单旧数据类型)。POD 类型与 C 语言兼容,需满足无自定义构造 / 析构函数、无虚函数、无基类、成员均为 POD 类型等(如基础类型、指针、POD 类型数组;若含自定义构造函数的类则不是)。

stl_alloc.h:内存分配的优化引擎

SGI STL 的架构设计中,对象构造前的内存空间申请、析构后的内存回收,均由 stl_alloc.h 模块负责。不同于简单封装 new/delete 的常规实现,SGI 为平衡分配效率内存碎片线程安全三大核心诉求,采用了分层优化的设计:将内存配置拆分为「第一级配置器」与「第二级配置器」,以 128 字节为阈值划分职责边界,当申请的内存区块大于 128 字节(大内存)时,直接调用第一级配置器;若小于等于 128 字节(小内存),则启用第二级配置器的内存池优化。

图片.png

其中,小内存分配通过「内存池 + 自由链表 + 8 字节对齐」的组合策略,大幅减少系统调用次数与内存碎片;大内存分配则直接封装系统 malloc/free/realloc,并针对性实现了 OOM(Out Of Memory,内存不足)的兜底逻辑。两类配置器的核心类名分别为:第二级配置器为__default_alloc_template;第一级配置器为__malloc_alloc_template

第一级配置器:__malloc_alloc_template

作为 SGI STL 内存配置体系的兜底核心分配器__malloc_alloc_template 直接封装系统级内存分配接口,核心设计目标是极致稳定性跨平台兼容性,专门处理大内存分配场景,并通过完善的内存不足(OOM)处理机制避免程序直接崩溃。以下是其完整源码与深度解析:

// 基于malloc的分配器:速度通常慢于下方的默认分配器,但线程安全且存储效率更高
#ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
# ifdef __DECLARE_GLOBALS_HERE
    void (* __malloc_alloc_oom_handler)() = 0;  // 全局OOM回调函数指针,默认空
    // 兼容g++ 2.7.2:该版本无法正确处理静态模板成员变量
# else
    extern void (* __malloc_alloc_oom_handler)();  // 外部声明,避免重复定义
# endif
#endif

// 模板参数__inst为实例化标记(通常传0),用于区分不同分配器实例
template <int __inst>
class __malloc_alloc_template {

private:
  // 内存不足时的兜底分配函数
  static void* _S_oom_malloc(size_t);
  static void* _S_oom_realloc(void*, size_t);

  // 编译器无模板成员缺陷时,将OOM回调指针作为类静态成员
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
  static void (* __malloc_alloc_oom_handler)();
#endif

public:
  // 内存分配核心接口
  static void* allocate(size_t __n)
  {
    void* __result = malloc(__n);  // 直接调用系统malloc
    // 分配失败则进入OOM处理流程
    if (0 == __result) __result = _S_oom_malloc(__n);
    return __result;
  }

  // 内存释放接口(第二个参数仅为接口统一保留,free无需内存大小)
  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
    // 重分配失败则进入OOM处理流程
    if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
    return __result;
  }

  // 设置OOM回调函数:返回旧函数指针,支持后续恢复
  static void (* __set_malloc_handler(void (*__f)()))()
  {
    void (* __old)() = __malloc_alloc_oom_handler;
    __malloc_alloc_oom_handler = __f;
    return __old;
  }

};

// 静态成员初始化:编译器无缺陷时,显式初始化OOM回调指针为NULL
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif

// OOM场景下的malloc重试逻辑
template <int __inst>
void* __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
    void (* __my_malloc_handler)();
    void* __result;

    for (;;) {  // 无限循环:直到分配成功或抛异常
        __my_malloc_handler = __malloc_alloc_oom_handler;
        // 无回调函数则抛出bad_alloc异常,终止循环
        if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
        // 调用用户自定义的OOM回调(如释放缓存、打印日志)
        (*__my_malloc_handler)();
        // 回调后再次尝试分配内存
        __result = malloc(__n);
        if (__result) return __result;  // 分配成功则返回
    }
}

// OOM场景下的realloc重试逻辑(与_S_oom_malloc逻辑一致)
template <int __inst>
void* __malloc_alloc_template<__inst>::_S_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;
    }
}

// 实例化:将模板参数固定为0,简化对外使用的类型名
typedef __malloc_alloc_template<0> malloc_alloc;
适配老旧编译环境

SGI STL 诞生于 C++ 标准尚未统一的年代,为了面向早期 C++ 编译环境开发,针对g++ 2.7.2 无法正确处理模板静态成员的缺陷,通过__STL_STATIC_TEMPLATE_MEMBER_BUG宏做双层适配以此全程兼顾历史兼容性与代码规范:

  • 当编译器存在该缺陷:将 __malloc_alloc_oom_handler(OOM 回调函数指针)声明为全局变量,避免模板类静态成员的编译问题;
  • 当编译器无此缺陷:将 OOM 回调指针作为类静态成员,保持面向对象的封装性。
遵循 STL 标准的极简接口封装

所有公有接口严格对齐 STL 分配器规范,底层直接封装系统调用,保证稳定性:

  • allocate:直接封装系统 malloc,分配指定字节数的内存,若分配失败则自动触发 OOM 兜底逻辑,避免直接返回 NULL;
  • deallocate:封装 free 完成内存释放,第二个参数对 free 无实际作用,仅为了与第二级配置器接口保持一致而保留,确保整个分配器体系调用逻辑统一;
  • reallocate:封装 realloc 实现内存大小调整,支持内存扩容 / 缩容,失败同样进入 OOM 流程;
  • __set_malloc_handler:支持用户设置自定义 OOM 回调函数,同时返回旧的回调指针,为了方便后续恢复,兼顾场景灵活性与使用便捷性。。
OOM的内存不足处理机制

区别于原生 malloc 分配失败直接返回 NULL 导致程序崩溃的问题,通过无限循环 + 回调 + 异常终止的逻辑实现兜底:用户可通过 __set_malloc_handler 设置自定义回调函数,当内存分配失败时,分配器会进入无限循环,先调用回调函数(用户可在其中释放缓存、关闭闲置资源等,为内存分配创造条件),随后立即重试 malloc/realloc;只有当未设置回调函数(指针为 NULL)时,循环才会终止并抛出 bad_alloc 异常。设计本质上是「不给程序崩溃的机会」,直到回调函数释放足够内存让分配成功,或无回调时通过异常终止流程,让内存不足的处理从「被动崩溃」变为「主动可控的无限重试」。

第二级配置器:__default_alloc_template

第二级配置器是 SGI-STL 空间配置器的性能核心,专门用于 ≤ 128 字节的小内存分配,旨在彻底解决频繁小内存分配带来的内存碎片与效率低下问题。它采用 内存池(Memory Pool)+ 自由链表(Free List)+ 8 字节对齐 的设计,将小内存分配从高频系统调用转化为用户态指针操作,性能实现数量级提升。以下是其完整源码与深度解析:

// 默认节点分配器。
// 在适配的编译器下,其效率与原始STL类专属分配器大致相当,但内存碎片更少。
// Default_alloc_template 的参数属于实验性特性,未来**可能会移除**。目前用户应直接使用 alloc。
//
// 核心实现特性:
// 1. 若客户端请求的对象大小 > _MAX_BYTES,直接通过 malloc 获取对象。
// 2. 其余情况均分配大小为 _S_round_up(请求大小) 的对象。因此客户端能获取足够的大小信息,
//    我们可将对象归还到正确的空闲链表,不会永久丢失对象的部分空间。
//

// 第一个模板参数指定是否允许多线程使用此分配器。
// 从一个 default_alloc 实例分配对象,用另一个实例释放是安全的。
// 这相当于将对象所有权转移给第二个实例。
// 这可能对内存引用局部性产生不良影响。
// 第二个模板参数未被使用,仅用于创建多个 default_alloc 实例。
// 注意:基于不同分配器实例的容器属于不同类型,这限制了该方式的实用性。

// 兼容老编译器的对齐与链表参数定义
#if defined(__SUNPRO_CC) || defined(__GNUC__)
// 若将这些定义为模板类成员会导致编译报错:
  enum {_ALIGN = 8};    // 内存对齐字节数
  enum {_MAX_BYTES = 128}; // 小额内存上限
  enum {_NFREELISTS = 16}; // 空闲链表个数(_MAX_BYTES/_ALIGN)
#endif

// 二级分配器模板类定义
template <bool threads, int inst>
class __default_alloc_template {

private:
  // 非老编译器的参数定义
  // 本应使用 static const int x = N,但多数编译器不支持,故用 enum 替代
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))
    enum {_ALIGN = 8};
    enum {_MAX_BYTES = 128};
    enum {_NFREELISTS = 16}; // 空闲链表个数
# endif

  // 内存对齐计算函数
  // 将字节数上调至 8 的整数倍(内存对齐)
  static size_t
  _S_round_up(size_t __bytes) 
    { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }

__PRIVATE:
  // 空闲链表节点联合体
  union _Obj {
        union _Obj* _M_free_list_link; // 指向下一空闲节点
        char _M_client_data[1];    /* 客户端实际使用的内存空间 */
  };

private:
// 空闲链表头指针数组声明
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    static _Obj* __STL_VOLATILE _S_free_list[]; 
        // 指定大小会导致 SunPro 4.1 编译器出现重复定义
# else
    static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; // 空闲链表数组
# endif

  // 计算对应空闲链表下标
  // 根据字节数获取对应空闲链表的下标
  static  size_t _S_freelist_index(size_t __bytes) {
        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
  }

  // 链表填充函数声明
  // 重新填充空闲链表:返回大小为 __n 的对象,多余节点加入对应空闲链表
  static void* _S_refill(size_t __n);
  // 内存池分配函数声明
  // 分配一块内存,用于存放 nobjs 个大小为 size 的对象
  // 若空间不足,nobjs 会被自动缩减
  static char* _S_chunk_alloc(size_t __size, int& __nobjs);

  // 内存池全局状态变量
  // 内存池状态变量
  static char* _S_start_free; // 内存池起始地址
  static char* _S_end_free;   // 内存池结束地址
  static size_t _S_heap_size; // 堆已分配总大小

// 多线程锁声明
# ifdef __STL_THREADS
    static _STL_mutex_lock _S_node_allocator_lock; // 线程互斥锁
# endif

    // 自动锁类声明
    // 本可使用 _STL_auto_lock,但无需空指针检查,且需判断线程是否真正启动
    class _Lock;
    friend class _Lock;
    // 自动加锁/解锁类(构造加锁,析构解锁)
    class _Lock {
        public:
            _Lock() { __NODE_ALLOCATOR_LOCK; }
            ~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
    };

public:

  /* __n 必须大于 0 */
  // 对外内存分配接口
  static void* allocate(size_t __n)
  {
    void* __ret = 0;

    // 超过小额内存上限,直接调用 malloc 分配
    if (__n > (size_t) _MAX_BYTES) {
      __ret = malloc_alloc::allocate(__n);
    }
    else {
      // 找到对应大小的空闲链表
      _Obj* __STL_VOLATILE* __my_free_list
          = _S_free_list + _S_freelist_index(__n);
      // 通过构造函数加锁,确保函数退出/栈展开时自动释放锁
#     ifndef _NOTHREADS
      /* 避免未使用变量警告 */
      _Lock __lock_instance;
#     endif
      _Obj* __RESTRICT __result = *__my_free_list;
      // 空闲链表无可用节点,重新填充
      if (__result == 0)
        __ret = _S_refill(_S_round_up(__n));
      else {
        // 取下空闲链表头节点
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }

    return __ret;
  };

  /* __p 不能为 0 */
  // 对外内存释放接口
  static void deallocate(void* __p, size_t __n)
  {
    // 超过小额内存上限,直接调用 free 释放
    if (__n > (size_t) _MAX_BYTES)
      malloc_alloc::deallocate(__p, __n);
    else {
      // 找到对应大小的空闲链表
      _Obj* __STL_VOLATILE*  __my_free_list
          = _S_free_list + _S_freelist_index(__n);
      _Obj* __q = (_Obj*)__p;

      // 加锁
#       ifndef _NOTHREADS
      /* 避免未使用变量警告 */
      _Lock __lock_instance;
#       endif /* _NOTHREADS */
      // 将释放的节点插回空闲链表头部
      __q -> _M_free_list_link = *__my_free_list;
      *__my_free_list = __q;
      // 此处自动释放锁
    }
  }

  // 对外内存重分配接口声明
  // 内存重分配
  static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);

} ;

// 定义默认分配器别名
// 默认分配器(支持多线程)
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
// 单线程专用分配器
typedef __default_alloc_template<false, 0> single_client_alloc;

// 分配器相等运算符重载
// 分配器相等判断(同类型分配器始终相等)
template <bool __threads, int __inst>
inline bool operator==(const __default_alloc_template<__threads, __inst>&,
                       const __default_alloc_template<__threads, __inst>&)
{
  return true;
}

// 分配器不等运算符重载
# ifdef __STL_FUNCTION_TMPL_PARTIAL_ORDER
// 分配器不等判断(同类型分配器始终不不等)
template <bool __threads, int __inst>
inline bool operator!=(const __default_alloc_template<__threads, __inst>&,
                       const __default_alloc_template<__threads, __inst>&)
{
  return false;
}
# endif /* __STL_FUNCTION_TMPL_PARTIAL_ORDER */



/* 以大块方式分配内存,避免 malloc 堆产生过多内存碎片 */
/* 假设 __size 已完成内存对齐 */
/* 已持有内存分配锁 */
// 内存池分配核心实现
template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, 
                                                            int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __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 >= __size) {
        __nobjs = (int)(__bytes_left/__size);
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    }
    // 内存池剩余空间不足,重新向堆申请内存
    else {
        // 申请大小:2倍需求 + 堆大小/16(对齐后)
        size_t __bytes_to_get = 
	  2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
        // 将内存池剩余碎片加入对应空闲链表
        if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                        _S_free_list + _S_freelist_index(__bytes_left);

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }
        // 向堆申请内存
        _S_start_free = (char*)malloc(__bytes_to_get);
        // malloc 分配失败,尝试从空闲链表回收空间
        if (0 == _S_start_free) {
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
	    _Obj* __p;
            // 遍历更大的空闲链表,尝试获取可用空间
            for (__i = __size;
                 __i <= (size_t) _MAX_BYTES;
                 __i += (size_t) _ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (0 != __p) {
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    // 重新尝试分配
                    return(_S_chunk_alloc(__size, __nobjs));
                }
            }
	    _S_end_free = 0;	// 异常时置空
            // 调用一级分配器,要么抛异常要么成功分配
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
        }
        // 更新堆大小和内存池边界
        _S_heap_size += __bytes_to_get;
        _S_end_free = _S_start_free + __bytes_to_get;
        // 重新执行分配逻辑
        return(_S_chunk_alloc(__size, __nobjs));
    }
}


/* 重新填充空闲链表:返回大小为 __n 的对象,多余节点加入对应空闲链表 */
/* 假设 __n 已完成内存对齐 */
/* 已持有内存分配锁 */
// 空闲链表填充实现
template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
    int __nobjs = 20; // 默认一次性分配20个节点
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    // 只分配到1个节点,直接返回,无需构建链表
    if (1 == __nobjs) return(__chunk);
    // 找到对应大小的空闲链表
    __my_free_list = _S_free_list + _S_freelist_index(__n);

    /* 在内存块中构建空闲链表 */
    __result = (_Obj*)__chunk;
    *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
    // 串联所有节点
    for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        // 最后一个节点置空
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
    }
    return(__result);
}

// 内存重分配实现
template <bool threads, int inst>
void*
__default_alloc_template<threads, inst>::reallocate(void* __p,
                                                    size_t __old_sz,
                                                    size_t __new_sz)
{
    void* __result;
    size_t __copy_sz;

    // 新旧大小均超过小额上限,直接调用 realloc
    if (__old_sz > (size_t) _MAX_BYTES && __new_sz > (size_t) _MAX_BYTES) {
        return(realloc(__p, __new_sz));
    }
    // 对齐后大小相同,无需重分配
    if (_S_round_up(__old_sz) == _S_round_up(__new_sz)) return(__p);
    // 分配新空间
    __result = allocate(__new_sz);
    // 拷贝数据(取较小值)
    __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz;
    memcpy(__result, __p, __copy_sz);
    // 释放旧空间
    deallocate(__p, __old_sz);
    return(__result);
}

// 多线程锁初始化
#ifdef __STL_THREADS
    template <bool __threads, int __inst>
    _STL_mutex_lock
    __default_alloc_template<__threads, __inst>::_S_node_allocator_lock
        __STL_MUTEX_INITIALIZER;
#endif

// 初始化内存池起始地址
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;

// 初始化内存池结束地址
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;

// 初始化堆已分配大小
template <bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;

// 初始化空闲链表数组
template <bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    _NFREELISTS
# else
    __default_alloc_template<__threads, __inst>::_NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// 16个0用于适配 SunPro 4.1 编译器,否则会为数组分配不足空间
编译器兼容与核心常量定义
  • _ALIGN = 8:所有小额内存皆按8 字节对齐,如申请 9 字节,实际分配 16 字节,适配 CPU 寻址规则,提升访问效率,避免未对齐访问的性能损耗;
  • _MAX_BYTES = 128:大小分流阈值,≤128 字节走二级配置器,>128 字节转发第一级配置器,直接用 malloc
  • _NFREELISTS = 16:16 条空闲链表,分别管理8/16/24/.../128字节的内存块(128÷8=16);
  • enum不用static const:兼容早期 C++ 编译器不支持静态常量编译期初始化的缺陷,enum 是折中方案。
零开销自由链表节点: union _Obj

采用联合体(union) 实现链表节点与用户数据的内存空间复用,让链表指针用户数据存储区共享同一块内存,实现零额外内存开销

union _Obj {
    union _Obj* _M_free_list_link;// 空闲态:串接自由链表
    char _M_client_data[1];// 必需成员:锚定用户数据起始地址,避免空联合体编译错误
};
  • 内存空闲时:_M_free_list_link 指向下一空闲块,串成链表;
  • 内存被使用时:整个对齐后的内存块(8/16/.../128 字节)直接交付用户,数据从 _M_client_data 地址开始存放。
内存对齐与自由链表下标计算

内存对齐函数_S_round_up在实现上把申请的字节数向上取整为 8 的倍数,即内存对齐,保证所有内存块都是 8 的倍数,匹配空闲链表的规格。公式原理为(bytes + 7) & ~7,比如申请 9 字节:(9+7)=1616 & ~7=16,最终分配 16 字节; 此外,找空闲链表下标函数_S_freelist_index在实现上则是根据对齐后的内存大小,找到对应空闲链表的下标(0~15) ,以此快速定位到对应规格的空闲链表,不用遍历所有链表,如8 字节→下标 0,16 字节→下标 1,...,128 字节→下标 15。

// 将字节数向上对齐到 8 的倍数
static size_t _S_round_up(size_t __bytes) {
    return (((__bytes) + (size_t)_ALIGN - 1) & ~((size_t)_ALIGN - 1));
}

// 根据大小获取对应自由链表的下标
static size_t _S_freelist_index(size_t __bytes) {
    return (((__bytes) + (size_t)_ALIGN - 1) / (size_t)_ALIGN - 1);
}
全局静态变量:内存池 + 空闲链表 + 线程安全
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;

template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;

template <bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;

template <bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[
# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    _NFREELISTS
# else
    __default_alloc_template<__threads, __inst>::_NFREELISTS
# endif
] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
  • _S_free_list16 个元素的 volatile 指针数组,每个元素对应一条空闲链表头,管理 8/16/…/128 字节小块内存;
  • _S_start_free / _S_end_free内存池的起始与结束地址,指向一整块连续内存;
  • _S_heap_size:记录配置器从操作系统堆中申请的总内存大小
  • 线程锁 + RAII_Lock类:多线程下,分配 / 释放内存时加锁,避免并发冲突;构造加锁,析构自动解锁,即使函数异常也不会死锁。
核心公有接口

二级空间配置器对外提供标准化、统一化的内存管理核心公有接口,包含内存分配、内存释放两大核心功能,配合自由链表填充、内存池分配等底层支撑接口,形成完整的内存管理闭环。接口严格遵循大小分流设计原则:大于 128 字节的大内存直接交由系统分配,小于等于 128 字节的小内存采用内存池 + 空闲链表高效管理,同时内置线程安全保障,为上层 STL 容器提供稳定、高效、无侵入的底层内存服务。

内存分配allocate

allocate二级空间配置器的核心对外内存分配接口,严格遵循 STL 分配器统一调用规范,根据申请内存的字节大小智能分流分配逻辑,实现小内存高效池化管理、大内存直接系统分配。

static void* allocate(size_t __n) {
    void* __ret = 0;
    if (__n > (size_t)_MAX_BYTES) {
        __ret = malloc_alloc::allocate(__n);
    } else {
        _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n);
        _Lock __lock_instance;
        _Obj* __result = *__my_free_list;
        if (__result == 0)
            __ret = _S_refill(_S_round_up(__n));
        else {
            *__my_free_list = __result->_M_free_list_link;
            __ret = __result;
        }
    }
    return __ret;
}

核心逻辑剖析

  • 返回值初始化:定义void* __ret = 0; 作为统一返回指针,初始化为空,用于存储最终分配的内存地址,保证接口返回值逻辑统一。
  • 大内存分流处理(>128 字节) :若申请字节数__n超过上限_MAX_BYTES(128),直接委托一级配置器malloc_alloc 执行分配,底层调用系统malloc,复用一级配置器的 OOM 兜底机制,无需二级配置器额外处理。
  • 统一返回结果:最终返回__ret,无论大 / 小内存分配路径,对外均返回合法内存指针,完全屏蔽底层不同分配逻辑的细节差异。

小内存池化分配(≤128 字节)

  • 定位目标空闲链表:通过_S_freelist_index(__n)计算对应规格的链表下标,从空闲链表数组_S_free_list中精准找到目标大小的空闲链表。
  • 检查链表可用内存:获取当前空闲链表的头节点__result,快速判断是否有可复用的内存块。
  • 链表空值兜底填充:若空闲链表无可用内存(__result == 0),调用_S_refill函数重新批量填充内存块(入参先通过_S_round_up完成 8 字节对齐),保证内存分配不中断。
  • 链表非空直接复用:若空闲链表有可用内存,直接摘取头节点,更新链表头指针指向后续节点,将该内存块作为分配结果,实现 O (1) 级快速分配。
内存释放deallocate

二级空间配置器的对外内存释放接口,与allocate接口对称设计,根据释放内存的大小智能分流释放逻辑。

static void deallocate(void* __p, size_t __n) {
    if (__n > (size_t)_MAX_BYTES) {
        malloc_alloc::deallocate(__p, __n);
    } else {
        _Obj* __q = (_Obj*)__p;
        _Obj* __STL_VOLATILE* __my_free_list = _S_free_list + _S_freelist_index(__n);
        _Lock __lock_instance;
        __q->_M_free_list_link = *__my_free_list;
        *__my_free_list = __q;
    }
}

核心逻辑剖析

  • 大内存释放处理(>128 字节) :若释放内存字节数__n超过上限_MAX_BYTES(128),直接委托一级配置器malloc_alloc 执行释放,底层调用系统free,复用一级配置器的释放逻辑,无额外二次处理。
  • 接口兼容性:接收内存指针__p和字节数__n两个参数,参数规格与一级配置器完全统一,保证整个分配器体系释放逻辑无差异。

小内存归还流程(≤128 字节)

  • 定位目标空闲链表:通过_S_freelist_index(__n)计算对应规格的链表下标,从空闲链表数组_S_free_list中精准找到内存归属的空闲链表,确保归位准确。
  • 指针类型转换:将通用void*类型的释放指针,强制转换为空闲链表节点类型_Obj*,适配链表节点的结构规范。
  • 多线程安全加锁:创建_Lock锁实例,对空闲链表操作加锁,保证多线程环境下内存归还操作的线程安全,避免并发竞争导致链表错乱。
  • 头插法快速归还:采用链表头插法将内存块归还至对应空闲链表,更新链表头指针指向归还的内存块,实现 O (1) 级快速归还,便于后续内存复用。
自由链表填充_S_refill

二级空间配置器的空闲链表补给核心函数,当对应规格的小内存空闲链表为空时自动触发,负责批量申请内存块并串接成空闲链表,解决内存池无可用内存的问题,保障分配流程不中断。

static void* _S_refill(size_t __n) {
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return __chunk;
    __my_free_list = _S_free_list + _S_freelist_index(__n);

    __result = (_Obj*)__chunk;
    *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
    for (__i = 1;; ++__i) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj->_M_free_list_link = 0;
            break;
        } else {
            __current_obj->_M_free_list_link = __next_obj;
        }
    }
    return __result;
}

核心逻辑剖析

  • 批量预设:默认一次性申请20 个相同大小(__n字节)的内存块,平衡内存池补给效率与内存碎片率。
  • 内存获取:调用底层内存分配函数 _S_chunk_alloc 向内存池申请连续内存块,同时接收实际分配到的内存块数量 __nobjs
  • 单块直接返回:若仅申请到1 块内存,无多余块可串接,直接返回该内存块即可。
  • 多块分流:若申请到多块内存,将第一块作为结果返回给调用方,剩余块串接成链表挂载到对应空闲链表,供后续分配复用。

内存块串接填充流程

  • 定位目标空闲链表:通过 _S_freelist_index(__n) 计算下标,精准找到需要补给的空闲链表。
  • 划分首块返回内存:将申请到的连续内存首块,作为本次补给的返回结果,直接供分配使用。
  • 批量串接链表:遍历剩余内存块,将每块内存的指针指向相邻下一块,把离散的内存块串成单向空闲链表。
  • 链表收尾置空:最后一块内存块的指针置为 0,标识空闲链表的结尾,避免指针越界。
  • 挂载链表完成补给:将串接好的空闲链表挂载到目标链表头,完成内存池补给,后续可直接 O (1) 级取用。
内存池核心分配_S_chunk_alloc

二级空间配置器的内存池底层核心分配函数,专为_S_refill提供批量内存支持,负责从内存池中切分连续内存块,是二级配置器内存池的实际内存供给源头。

static char* _S_chunk_alloc(size_t __size, int& __nobjs) {
    char* __result;
    size_t __total_bytes = __size * __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 >= __size) {
        __nobjs = (int)(__bytes_left / __size);
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return __result;
    } else {
        size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
        if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                _S_free_list + _S_freelist_index(__bytes_left);
            ((_Obj*)_S_start_free)->_M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }
        _S_start_free = (char*)malloc(__bytes_to_get);
        if (_S_start_free == 0) {
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
            _Obj* __p;
            for (__i = __size; __i <= (size_t)_MAX_BYTES; __i += (size_t)_ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (__p != 0) {
                    _S_start_free = (char*)__p;
                    *__my_free_list = __p->_M_free_list_link;
                    _S_end_free = _S_start_free + __i;
                    return _S_chunk_alloc(__size, __nobjs);
                }
            }
            _S_end_free = 0;
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
        }
        _S_heap_size += __bytes_to_get;
        _S_end_free = _S_start_free + __bytes_to_get;
        return _S_chunk_alloc(__size, __nobjs);
    }
}

核心逻辑剖析

  • 三级分配策略:根据内存池剩余空间的充足程度,按内存充足、部分充足、完全不足三级场景逐级处理,最大化复用内存池现有资源,减少系统内存申请频次。
  • 兜底保障机制:内存池空间耗尽时,优先向系统堆扩容;系统扩容失败则回收空闲链表闲置内存;终极兜底触发一级配置器 OOM 处理机制,确保内存分配流程永不中断。
  • 关键变量_S_start_free标记内存池当前可用起始地址,_S_end_free标记内存池结束地址,__bytes_left标识内存池剩余可用空间。

内存池三级分配流程

  • 第一级(内存完全充足) :内存池剩余空间≥所需总字节数,直接从内存池头部切分对应大小的连续内存,更新内存池起始指针后立即返回分配地址,实现高效分配。
  • 第二级(内存部分充足) :内存池剩余空间不足总需求,但可容纳≥1 个完整内存块,重新计算最大可分配块数,切分全部有效内存并修正实际分配块数,充分利用剩余内存空间后返回地址。
  • 第三级(内存完全不足) :先将内存池剩余的小内存碎片归还至对应规格空闲链表,避免内存浪费;再向系统调用malloc申请 2 倍需求总量 + 动态增量的内存扩充内存池,更新内存池边界与总堆大小;若系统malloc分配失败,遍历更大规格空闲链表回收闲置内存块复用;仍无可用内存则调用一级配置器触发 OOM 兜底;内存池更新完成后递归调用自身,重新执行分配流程。
上层包装适配器 simple_alloc

SGI-STL 分配器体系的上层对象级适配包装器,作为底层字节级配置器与上层 STL 容器的衔接桥梁,实现字节分配到对象分配的无缝转换,为容器提供类型安全、简洁易用的内存管理接口。

template <class _Tp, class _Alloc>
class simple_alloc {
public:
    static _Tp* allocate(size_t __n)
      { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }
    static _Tp* allocate(void)
      { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }
    static void deallocate(_Tp* __p, size_t __n)
      { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }
    static void deallocate(_Tp* __p)
      { _Alloc::deallocate(__p, sizeof (_Tp)); }
};

核心逻辑剖析

  • 适配衔接作用:作为中间适配层,屏蔽底层字节级分配的实现细节,向上提供面向对象的内存操作接口,向下复用一级、二级空间配置器的底层分配能力。
  • 类型安全保障:将底层分配器返回的无类型void*指针,强制转换为指定对象类型指针,杜绝内存类型误用,实现分配过程的类型安全。
  • 接口极简设计:自动封装sizeof字节数计算逻辑,上层容器仅需指定对象个数,无需关注底层字节换算,大幅降低使用成本。
  • 静态高效特性:所有成员函数均为静态成员,无需创建类实例,直接通过类名调用,无额外对象开销,轻量高效。

核心功能流程

  • 对象内存分配:接收上层容器传入的对象个数,自动计算对应总字节数,调用底层配置器完成内存分配,将返回的无类型指针转换为对象指针后返回;同时提供批量分配、单个对象分配两种便捷接口。
  • 对象内存释放:接收待释放的对象指针与对象个数,自动换算总字节数后调用底层配置器释放内存;支持批量释放、单个对象释放,过滤无效的 0 数量释放操作,避免异常操作。

总结

结合以上分析可见,SGI-STL 空间配置器正是以内存分配与对象构造、析构分离为核心设计思想,采用两级配置器架构:一级配置器封装 malloc/free 并提供 OOM 兜底机制,专门处理大内存分配;二级配置器则以内存池 + 自由链表 + 8 字节对齐的方案实现小内存的高效管理;同时配合 iterator_traits 迭代器萃取与 __type_traits 类型特性,完成编译期类型判断与 trivial 析构函数的性能优化;最终通过 simple_alloc 对外提供类型安全的对象级内存接口,整体实现了高效、低碎片、高稳定性的工业级内存管理方案。