SGI-STL 空间配置器深度源码剖析
引言:空间配置器的概述
SGI-STL 的源码我早年便已研读,该版本源代码可读性极佳,特此记录笔记以便后续查阅,接下来深入解析其设计实现的核心技巧。
从 STL 使用角度而言,空间配置器是最无需关注的组件,它始终隐藏在容器背后默默工作;但从 STL 实现角度而言,空间配置器是第一个需要理解的核心组件 ——STL 的所有数据都存储在容器中,而容器必须配置空间来存储数据;因此理解容器实现的前提,是掌握空间配置器的核心原理。
下文将从源码层面深度剖析 SGI-STL 空间配置器的实现机制。
空间配置器的核心设计:分离内存管理与对象生命周期
STL 空间配置器负责容器的内存分配 / 释放、对象构造 / 析构,直接决定 STL 的性能与可靠性。SGI-STL 空间配置器的核心设计原则:将内存分配 / 释放与对象构造 / 析构彻底分离。
其定义在<memory>文件中,SGI<memory>包含两个核心头文件,分离原则正体现在二者的分工上:
stl_alloc.h:专注原始内存的分配与释放(字节级操作);stl_construct.h:负责对象的构造与析构(类型相关操作)。
stl_construct.h:对象生命周期的控制器
stl_construct.h实现对象构造与析构的核心逻辑,通过两个核心函数完成类型感知操作:构造函数_Construct()、析构函数_Destroy()。
构造函数: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_type为int)。 整个萃取过程仅发生在编译期,核心目标是获取迭代器指向的元素类型信息,而非返回运行时可访问的有效指针。
底层实现本质
__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,但会通过特化版本为基础类型(如 int、char 等)赋予正确值。其字段解析如下:
this_dummy_member_must_be_first:特殊标记成员,不表示任何类型特性,用于告知编译器该__type_traits是STL内部特殊模板,以区分用户自定义的同名模板,确保编译器自动特化时正确识别。has_trivial_default_constructor:标识类型是否有trivial 默认构造函数。trivial 默认构造函数由编译器自动生成,无自定义逻辑,仅完成成员默认初始化(如int、char等基础类型;若自定义类显式定义默认构造函数则为非 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 字节(小内存),则启用第二级配置器的内存池优化。
其中,小内存分配通过「内存池 + 自由链表 + 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)=16 → 16 & ~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_list:16 个元素的 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 对外提供类型安全的对象级内存接口,整体实现了高效、低碎片、高稳定性的工业级内存管理方案。