一、代码结构
先看一段伪代码
class Foo{...};
Foo* pf = new Foo; //配置内存,然后析构对象
delete pf; //将对象析构
new算式内含两阶操作:
- 调用::operator new 配置内存。
- 调用Foo::Foo()构造对象内容。
delete算式也内含两阶操作:
- 调用Foo::~Foo()将对象析构。
- 调用::operator delete 释放内存。
为精密分工,STL allocator决定将这两阶段操作区分开来。
【内存操作】
- 由 alloc::allocate()负责配置。
- 由 alloc::deallocate()负责释放。
【对象操作】
- 由 ::construct()负责构造。
- 由 ::destroy()负责析构。
STL标准将配置器定义于<memory>
之中。文件结构如下:
上述初步说了下内存配置与对象构造和对象析构在STL结构中的关系。接下来着重讲解内存配置相关。
二、空间配置器(std::alloc)
2.1 背景
在软件开发过程中因程序需求,不可避免的使用很多的小块内存(基本类型以及小内存的自定义类型),于是乎就会在程序中动态的申请和释放,这样就会产生如下问题:
- 内存碎片。(这里指外碎片)
- 一直因小块内存而进行内存申请、释放,这样会产生性能问题。
注意:
【内碎片】
指因为内存对齐/访问效率(CPU取址次数)而产生,如用户需要3字节,实际得到4或者8字节,
其中的多余大小空间就是浪费掉的。
【外碎片】
系统内存总量足够,但是不连续,所以无法分配给用户使用而产生的浪费。
2.2 设计
SGI STL对空间配置器的设计哲学如下:
- 向system heap要求空间。
- 考虑多线程(multi-threads)状态。
- 考虑内存不足时的应变措施。
- 考虑过多“小型区块”可能造成的内存碎片问题。
这里讨论的过程未包括多线程处理状态。
C++的内存配置基本操作是 ::operator new()
,内存释放基本操作是::operator delete()
。这两个全局函数相当于C的malloc()
和free()
函数。SGI正是以malloc()
和free()
完成内存的配置和释放。
为解决小型区块可能造成的内存碎片问题,SGI设计了双层配置器:
- 第一级配置器直接使用
malloc()
和free()
。 - 第二级配置器则视情况采用不同的策略,当配置区块超过128bytes时,则调用第一级配置器,当配置区块小于128bytes时,为降低额外负担则采用复杂的memory pool整理方式,而不再求助于第一级配置器。
至于是否只开放第一级配置器,还是同时开放第二级配置器,取决于__USE_MALLOC是否被定义。
SGI STL并未定义,所以SGI是第一级和第二级都开放的。
# ifdef __USE_MALLOC
...
typedef __malloc_alloc_template<0> malloc_alloc;
typedef malloc_alloc alloc; // 令 alloc 為第一級配置器
# else
...
// 令 alloc 為第二級配置器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
#endif /* ! __USE_MALLOC */
2.3 配置器关系
2.4 配置器接口包装和运用方式
参考链接:
https://www.cnblogs.com/lang5230/p/5556611.html