对SGI空间配置器的背景介绍可参考此篇博文,空间配置器背景介绍。
这篇主要总结下SGI空间配置器的第一级配置器。
一、源码剖析
#if 0
# include <new>
# define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
# include <iostream.h>
# define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
#endif
template <int inst>
class __malloc_alloc_template {
private:
//以下函数将用来处理内存不足的情况
static void *oom_malloc(size_t);
static void *oom_realloc(void *, size_t);
#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_malloc()
if (0 == result) result = oom_malloc(n);
return result;
}
static void deallocate(void *p, size_t /* n */)
{
//第一级配置器直接使用free()
free(p);
}
static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
{
//第一级配置器直接使用realloc()
void * result = realloc(p, new_sz);
//当申请不到内存时,改用oom_realloc()
if (0 == result) result = oom_realloc(p, new_sz);
return result;
}
//以下仿真C++的set_new_handler()。换句话说,你可以通过它
//指定你自己的out-of-memory handler。
static void (* set_malloc_handler(void (*f)()))()
{
void (* old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return(old);
}
}
// malloc_alloc out-of-memory handling
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int inst>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0; //初值为零
#endif
template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
{
void (* my_malloc_handler)();
void *result;
//不断尝试释放、配置、再释放、再配置...
//释放函数通过set_malloc_handler指定。
for (;;) {
my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }
(*my_malloc_handler)();//调用处理例程,企图释放内存。
result = malloc(n); //再次尝试配置内存。
if (result) return(result);
}
}
template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
{
void (* my_malloc_handler)();
void *result;
//不断尝试释放、配置、再释放、再配置...
//释放函数通过set_malloc_handler指定。
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);
}
}
typedef __malloc_alloc_template<0> malloc_alloc;
//SGI对alloc接口的再次包装,使配置器接口能够符合STL规格。
//simple_alloc内部的四个成员函数都是单纯的转调用,调用传递给
//配置器(可能是第一级,也可能是第二级)的成员函数。SGI STL容器
//全部使用这个simple_alloc接口。
template<class T, class Alloc>
class simple_alloc {
public:
static T *allocate(size_t n)
{ return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
static T *allocate(void)
{ return (T*) Alloc::allocate(sizeof (T)); }
static void deallocate(T *p, size_t n)
{ if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
static void deallocate(T *p)
{ Alloc::deallocate(p, sizeof (T)); }
};
二、总结
1、第一级配置器以malloc()
、free()
、realloc()
等C函数执行实际的内存配置、释放、重配置等操作,并实现出类似C++ new handler的机制。配置器不是直接使用::operator new
来配置内存的,所以不能直接运用C++ new handler机制。
2、所谓C++ new handler机制,指你可以要求系统在内存配置需求无法被满足时,调用一个你指定的函数。换句话说,一旦::operator new
无法完成任务,在丢出std::bad_alloc
异常状态前,会先调用由客端指定的处理例程。该处理例程通常即被称为new-handler。另new-handler解决内存不足的做法是有特定的模式的。
这里举个例子:
void outOfMem()
{
std::cerr << "Unable to satisfy request for memory\n";
std::abort()
}
int main()
{
std::set_new_handler(outOfMem);
int* pBigDataArray = new int[100000000L];
...
}
本例中,如果operator new
无法为100,000,000个整数分配足够的空间,outOfMem
则会被调用。
3、SGI以malloc
而非::operator new
来配置内存,作者认为两个原因,一是历史因素,二是C++并未提供相应于realloc()
的内存配置操作,因此SGI不能直接使用C++的set_new_handler()
,必须仿真一个类似的set_malloc_handler()
,查看SGI STL源码,好像都没设置这个参数,也就是说内存不足时,直接跑异常。
4、SGI第一级配置器allocate()和realloc()都是在调用malloc()和realloc()失败后,改调用oom_malloc()和oom_realloc()。后两者都有内部循环,不断调用"内存不足处理例程",期望在某次调用之后,获得足够的内存而圆满完成任务,若"内存不足处理例程"并未被客端设定,oom_malloc()和oom_realloc()则会抛出bad_alloc异常信息,或利用exit(1)硬生生中止程序。
设计“内存不足处理例程”是客端的责任,设定“内存不足处理例程”也是客端的责任。另外“内存不足处理例程”解决问题的做法有着特定的模式,使用者要多注意。