SGI STL 空间配置器
Posted Jqivin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SGI STL 空间配置器相关的知识,希望对你有一定的参考价值。
一、空间配置器
当我们使用stl标准容器的时候,我们就在用空间配置器。空间配置器总是隐藏在背后,默默的付出。但我们却没有感受的到,其实,在vector,list等标准容器在实现的时候,就指定了默认的空间配置器。
二、为什么使用空间配置器?
当我们在使用new分配空间的时候,new有三种使用方法(new的三种用法:new的使用)。
当我们使用new关键字构建一个对象的时候,无论我们申请多大的空间,都会有一些越界标志和头部信息,大大降低了空间的利用率,所以,我们如果能在申请大空间的时候,使用new关键字,申请小空间的时候使用内存池的空间直接分配。这样就大大减少了空间的浪费。(内存碎片)
当我们使用关键字定义对象的时候,有两个步骤:调用::operator new申请空间,调动构造函数构建对象。当我们使用delete删除对象的时候,也有两个操作:调用析构函数析构对象,调用::operator deleter释放内存。
STL是怎么做的呢?
(SGI的alloc的做法)
STL进行了分离,第一个头文件只管理空间(内存,申请空间,释放空间)就相当于operator new;
第二个头文件只管理对象(构造和析构)。下图
第三个头文件是管理大块内存
三、construct.h
四、std::alloc的实现
关于std::alloc
std::alloc是SGI的一个特殊的空间配置器,SGI标准的空间配置器是std::allocator但是SGI却没有使用过它,因为这个标准的配置器只是简单的对operator new和operator delete做一个简单的封装,效率很低下。
stl_alloc.h 空间的配置与释放
介绍
考虑到小型区块所可能造成的内存破碎问题,SGl设计了双层级配置器,一级配置器直接使用malloc ()和free() .第二级配置器则视情况采用不同的策略,如果要申请的内存大于128bytes,视之为“足够大”,调用一级配置器。否则,采用memory pool的方式进行内存的分配。
总之,
(1)一级配置器:malloc free
(2)二级配置器:采用不同的策略,空间大调用一级配置器,空间小调用内存池。
一级配置器
主要的接口有四个
:
1.
static void allocate(size_t n)
{
//若果有内存,即调用malloc成功;
//如果没有内存,调用私有成员oom_malloc();
}
2.
static void reallocate(void* p,size_t new)
{
//若果有内存即realloc(n)成功;
//如果没有内存,调用私有成员oom_realloc();
}
3.
static void* deallocate(void* p,size_t new)
{
free(p);
}
4.
set_alloc_handler() //设置内存不足的处理函数
代码实现
:
namespace jqw
{
#if 0
#include<new>
#define _THROW_BAD_ALLOC throw std::bad_alloc;
#elif !defined(_THROW_BAD_ALLOC)
//#include<iostream>
#define _THROW_BAD_ALLOC std::cerr<<"out of memory" <<std::endl;exit(1)
#endif
//非类型模板化,inst没有任何意义
template <int inst>
class _malloc_alloc_template
{
private:
static void* oom_malloc(size_t n)
{
void (*my_malloc_handler)() = nullptr;
void* result = nullptr;
for (;;)
{
my_malloc_handler = __malloc_alloc_oom_handler;
if (my_malloc_handler == nullptr)
{
_THROW_BAD_ALLOC;
}
(*my_malloc_handler)();
result = malloc(n);
//如果申请到了空间,就返回申请到的空间,否则再次进入循环,继续释放空间
if (result != nullptr)
{
return result;
}
}
}
static void* oom_realloc(void* p, size_t new_sz)
{
void (*my_malloc_handler)() = nullptr;
void* result = nullptr;
for (;;)
{
my_malloc_handler = __malloc_alloc_oom_handler;
if (my_malloc_handler == nullptr)
{
_THROW_BAD_ALLOC;
}
(*my_malloc_handler)();
result = realloc(p, new_sz);
//如果申请到了空间,就返回申请到的空间,否则再次进入循环,继续释放空间
if (result != nullptr)
{
return result;
}
}
}
//返回值为void,无参的函数指针,在类外把它初始化为空 -- 静态成员初始化要在类外
static void(*__malloc_alloc_oom_handler)();
public:
//申请空间,如果检测到有内存调用malloc,没有就调用oom_malloc
static void* allocate(size_t n)
{
void* result = malloc(n);
if (result == nullptr)
{
result = oom_malloc(n);
}
return result;
}
//释放空间,一级配置器直接使用free()
static void* deallocate(void* p, size_t /* n */)
{
free(p);
}
//重新分配空间
static void* reallocate(void* p, size_t /* old_sz */, size_t new_size)
{
void* result = realloc(p, new_size);
if (result == nullptr)
{
result = oom_realloc(p, new_size);
}
return result;
}
typedef void(*malloc_handler)(); //定义一个函数指针
//返回值是一个函数指针,参数也是一个函数指针
//static void(*set_alloc_handler)(void(*f)()); //与下面的等价
//在这个函数的作用是吧原来的__malloc_alloc_oom_handler返回(可能别的函数收到这个返回值就把这个函数指针的内容运行 -- 释放空间)
//然后再把传过来的指针给给__malloc_alloc_oom_handler,供下次使用
static malloc_handler set_alloc_handler(malloc_handler f)
{
malloc_handler old = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return old;
}
};
//静态成员的类外定义
template <int inst>
void(*_malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = nullptr;
typedef _malloc_alloc_template<0> malloc_alloc;
}
一级配置器的测试
:首先我们定义两个全局指针变量用来处理内存不足的情况,也就是储备内存空间。
//一级配置器的测试代码
char* p1 = (char*)malloc(1024 * 1024 * 200);
char* p2 = (char*)malloc(1024 * 1024 * 100);
void jqwfun2()
{
free(p2);
p2 = nullptr;
jqw::malloc_alloc::set_alloc_handler(nullptr);
}
void jqwfun1()
{
free(p1);
p1 = nullptr;
jqw::malloc_alloc::set_alloc_handler(jqwfun2);
}
int main()
{
jqw::malloc_alloc::set_alloc_handler(jqwfun1);
int* p = (int*)jqw::malloc_alloc::allocate(sizeof(int));
*p = 100;
free(p);
return 0;
}
如果把allocate中的result置为空,执行到oom_malloc的时候,my_malloc_handler就变成了jqwfun1.
注意
SGI第一级配置器的allocate ()和reallocate ()都是在调用malloc ()和realloc ()不成功后,改调用oom_malloc()和oom_realloc ()。后两者都有内循环,不断调用"内存不足处理例程",期望在某次调用之后,获得足够的内存而圆满完成任务。但如果"内存不足处理例程"并未被客端
设定,oom_malloc()和oom_realloc ()便老实不客气地调用_THROW_BAD_ALLOC,丢出 bad_alloC异常信息,或利用exit(1)硬生生中止程序。
二级配置器
参考文章:std::alloc二级配置器
接口
整个计究竟只开放第一级配置器,或是同时开放第二级配置器,取决于_USE_MALLOc6是否被定义(唔,我们可以轻易测试出来,SGI STL并未定义_USE_MALLOC) :
书籍:《STL源码剖析 - 侯捷》
以上是关于SGI STL 空间配置器的主要内容,如果未能解决你的问题,请参考以下文章