stl空间配置器alloc
new运算包含两阶段操作:
1) 调用::operator new分配内存 2) 调用构造函数构造对象内容
delete运算包含两阶段操作:
1)调用析构函数将对象析构 2)调用::operator delete释放内存
stl内存配置操作由allocate()负责,内存释放操作由deallocate()负责;对象构造操作由construct()负责,对象析构操作由destroy()负责。
template <class T1, class T2>
inline void construct(T1 *p, const T2 &value) {
// placement new,将初值value设定到指针p所指的空间
new(p) T1(value);
} template <class T>
inline void destroy(T* pointer) {
// 显式的调用类的析构函数销毁对象,内存空间不会被释放
pointer->~T();
}
SGI设计了双层级配置器,第一级配置器__malloc_alloc_template直接使用malloc()和free(),第二级配置器__default_alloc_template视情况采用不同的策略:当配置区块超过128bytes时,调用第一级配置器,当配置区块小于128bytes时,采用内存池方式
//SGI第一级配置器
template<int inst>
class __malloc_alloc_template
{
private:
//处理内存不足的情况
//c++ new handler机制:系统在内存配置需求无法被满足时,调用一个指定函数,参见effective c++条款7
static void *oom_malloc(size_t);
static void *oom_realloc(void *, size_t);
static void(*__malloc_alloc_oom_handler)();//由用户设定,new_handler函数 public:
//第一级配置器直接使用malloc()
static void *allocate(size_t n)
{
void *result = malloc(n);
if (result == )
result = oom_malloc(n);
return result;
}
//第一级配置器直接使用free()
static void deallocate(void *p, size_t)
{
free(p);
}
//第一级配置器直接使用realloc
static void *reallocate(void *p, size_t, size_t new_sz)
{
void *result = realloc(p, new_sz);
if (result == )
result = oom_realloc(p, new_sz);
return result;
}
//仿真c++的set_new_handler()
static void(*set_malloc_handler(void(*f)()))()
{
void(*old) = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return old;//恢复全局new_handler
}
};
//初值为0,由用户设定
template<int inst>
void(*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = ; template<int inst>
void *__malloc_alloc_template<inst>::oom_malloc(size_t n)
{
void(*my_malloc_handler());
void *result;
// 无限循环,尝试分配内存。先判断new_handler是否为空,若非空,调用new_handler,然后重新分配内存
for (;;)
{
my_malloc_handler = __malloc_alloc_oom_handler;
if ( == 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;
//先设置处理例程,尝试释放内存,然后再次尝试分配内存
for (;;)
{
my_malloc_handler = __malloc_alloc_oom_handler;
if ( == my_malloc_handler)
{
throw bad_alloc;
}
(*my_malloc_handler)();//同上
result = realloc(p, n);
if (result)
return result;
}
} typedef __malloc_alloc_template<> malloc_alloc;
-
使用16个链表来维护大小不同的空闲内存块,分配小块内存时,从链表中划拨相应的块即可
enum{ __ALLGN = };//小块的上调边界
enum{ __MAX_BYTES = };//小块的上限大小
enum{ __NFREELISTS = __MAX_BYTES / __ALLGN };//free_lists个数 //第二级空间配置器,threads判断是否用于多线程,第二参数无用
template<bool threads, int inst>
class __default_alloc_template
{
private:
//将请求内存大小上调至8的倍数,即将低三位加上7向高位进位,然后将低三位的余数变为0
static size_t ROUND_UP(size_t bytes)
{
return (((bytes)+__ALLGN - )&~(__ALLGN - ));//最后三位为0一定是8的倍数
}
//当区块已分配给用户后,free_list_link指针不再使用,不会浪费空间
union obj
{
union obj *free_list_link;
char client_data[];
};
static obj *volatile free_list[__NFREELISTS];//16个链表表头指针
//确定使用几号free-list
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes)+__ALLGN - ) / __ALLGN - );
}
//当free-list没有可用区块时,为free-list重新填充空间,新的空间取自内存池,调用chunk_alloc函数
static void *refill(size_t n);
//从内存池中取空间给freelist
static char *chunk_alloc(size_t size, int &nobjs); static char *start_free;//内存池起始位置
static char *end_free;//内存池结束位置
static size_t heap_size; public:
static void *allocate(size_t n);
static void deallocate(void *p, size_t n);
static void *reallocate(void *p, size_t old_sz, size_t new_sz);
}; template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::start_free = ; template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = ; template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::heap_size = ; template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::free_list[__NFREELISTS] = { , , , , , , , , , , , , , , , }; //空间配置函数allocate()
template<bool threads, int inst>
void *__default_alloc_template<threads, inst>::allocate(size_t n)
{
obj * volatile *my_free_list;
obj *result; //大于128调用第一级配置器
if (n > __MAX_BYTES)
{
return (malloc_alloc::allocate(n));
}
//寻找合适的free-list
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;//free list中某链表的头指针
if (result == )
{
//没找到可用的free list,准备重新填充free list
void *r = refill(RAND_MAX(n));
return r;
}
//调整free-list
*my_free_list = result->free_list_link;
return result;
} //空间释放函数deallocate()
template<bool threads, int inst>
void __default_alloc_template<threads, inst>::deallocate(void *p, size_t n)
{
obj *q = (obj *)p;
obj *volatile *my_free_list; if (n > (size_t)__MAX_BYTES)
{
malloc_alloc::deallocate(p, n);
return;
} my_free_list = free_list + FREELIST_INDEX(n);
//*my_free_list为数组元素,链表头指针
q->free_list_link = *my_free_list;
*my_free_list = q;
} //当free list中没有可用区块时,refill()为free list重新填充空间
template<bool threads, int inst>
void *__default_alloc_template<threads, inst>::refill(size_t n)
{
//新的空间取自内存池,缺省取得20个新区块
int nobjs = ;
char *chunk = chunk_alloc(n, nobjs);
obj * volatile *my_free_list;
obj *result;
obj *current_obj, *next_obj;
int i;
//只获得一个区块,这个区块分配给调用者
if ( == nobjs)
return chunk; my_free_list = free_list + FREELIST_INDEX(n); result = (obj *)chunk;//返回给用户
//在chunk空间内建立free list
*my_free_list = next_obj = (obj *)(chunk + n);
for (i = ;; i++)
{
current_obj = next_obj;
next_obj = (obj *)((char *)next_obj + n);
if (nobjs - == i)
{
current_obj->free_list_link = ;
break;
}
else
{
current_obj->free_list_link = next_obj;
}
}
return result;
} template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int &nobjs)
{
char *result;
size_t total_bytes = size*nobjs;
size_t bytes_left = end_free - start_free; //内存池剩余空间完全满足需求
if (bytes_left >= total_bytes)
{
result = start_free;
start_free += total_bytes;
return result;
}
//内存池剩余空间足够供应一个以上的区块
else if (bytes_left >= size)
{
nobjs = bytes_left / size;
total_bytes = size*nobjs;
result = start_free;
start_free += total_bytes;
return result;
}
else
{
//内存池剩余空间连一个区块的大小都无法提供
//从堆中配置内存,大小为需求量的两倍再加上一个随着配置次数增加而愈来愈大的附加量
size_t bytes_to_get = * total_bytes + ROUND_UP(heap_size >> );
//尝试利用内存池残余
if (bytes_left > )
{
obj * volatile *my_free_list = free_list + FREELIST_INDEX(bytes_left);
((obj *)start_free)->free_list_link = *my_free_list;
*my_free_list = (obj *)start_free;
} //补充内存池
start_free = (char *)malloc(bytes_to_get);
if ( == start_free)
{
//heap空间不足
int i;
obj *volatile *my_free_list, *p;
//在free-list中搜寻未用且足够大的区块释放至内存池,然后再分配
for (i = size; i <= __MAX_BYTES; i += __ALLGN)
{
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if ( != p)
{
*my_free_list = p->free_list_link;
start_free = (char *)p;
end_free = start_free + i;
//递归调用chunk_alloc,为了修正nobjs
return (chunk_alloc(size, nobjs));
}
}
//到处都没内存可用
end_free = ;
//调用第一级配置器,要么抛出异常,要么改善内存不足的情况
start_free = (char *)malloc_alloc::allocate(bytes_to_get);
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return (chunk_alloc(size, nobjs));
}
} //SGL为alloc包装一个接口,alloc为第一级或第二级配置器模板类,SGISTL容器都使用simple_alloc接口
template <class T,class Alloc>
class simple_alloc
{
public:
static T* allocate(size_t n)
{
return n == ? : (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 (n != )
Alloc::deallocate(p, n*sizeof(T));
}
static void deallocate(T *p)
{
Alloc::deallocate(p, sizeof(T));
}
}; //SGL容器使用配置器的方式:
//第二个template参数所接受的缺省参数alloc,可以是第一级配置器,也可以是第二级配置器
template<class T,class Alloc=alloc>
class vector
{
public:
typedef T value_type;
//...
protected:
//专属的空间配置器
typedef simple_alloc<value_type, Alloc>data_allocator;
//...
};
stl空间配置器alloc的更多相关文章
- stl空间配置器线程安全问题补充
摘要 在上一篇博客<STL空间配置器那点事>简单介绍了空间配置器的基本实现 两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等. 在最后,又关 ...
- 【转】STL空间配置器
STL空间配置器(allocator)在所有容器内部默默工作,负责空间的配置和回收.STL标准为空间配置器定义了标准接口(可见<STL源码剖析>P43).而具体实现细节则由各编译器实现版本 ...
- STL——空间配置器(构造和析构基本工具)
以STL的运用角度而言,空间配置器是最不需要介绍的东西,它总是隐藏在一切组件(更具体地说是指容器,container)的背后,默默工作,默默付出.但若以STL的实现角度而言,第一个需要介绍的就是空间配 ...
- STL空间配置器
1.什么是空间配置器? 空间配置器负责空间配置与管理.配置器是一个实现了动态空间配置.空间管理.空间释放的class template.以内存池方式实现小块内存管理分配.关于内存池概念可以点击:内存池 ...
- 咬碎STL空间配置器
STL空间配置器 一.开场白: 给我的感觉就是,了解是空间配置器的功能,是那么的明了:在看原理,我还是很开心:接下来是360度大转变: 那么长的变量或者函数命名.那么多的宏.不爽,不过,遇上我这种二货 ...
- STL空间配置器那点事
STL简介 STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其 ...
- STL空间配置器解析和实现
STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:(一)STL ...
- 【陪你系列】5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码
大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreT ...
- STL——空间配置器(SGI-STL)
一. 空间配置器标准接口 参见<STL源码剖析>第二章-2.1.<memory>文件. 二.具备次配置力的SGI空间配置器 1. SGI STL的配置器与众不同,也与标准规范不 ...
随机推荐
- 前端--3、jQuery
介绍 jQuery是一个Javascript框架.其宗旨是——WRITE LESS,DO MORE! 是轻量级的js库,兼容CSS3和各种浏览器. 作用:处理HTMLdocuments.events. ...
- 转载pcb设计详细版
http://www.51hei.com/bbs/dpj-52438-1.html 详细的altium designer制作PCB步骤,按照步骤一步步的学习就会自己制作PCB模型 目 录 实验三 层 ...
- 2) 十分钟学会android--建立第一个APP,执行Android程序
通过上一节课创建了一个Android的Hello World项目,项目默认包含一系列源文件,它让我们可以立即运行应用程序. 如何运行Android应用取决于两件事情:是否有一个Android设备和是否 ...
- glassfish中新建数据源(创建数据库连接池)
1.浏览器输入:http://localhost:4848 登录glassfish域管理控制台,默认的用户名和密码是amin和adminadmin.(也可以通过NetBeans的服务选项卡--服务器- ...
- 如何在Win8.1和Win2012上运用PowerShell快速生成、安装、导出自签名证书 (Self-Signed Certificate)
自签名证书用途很广,测试,开发,本地或者云端网站(比如Microsoft Azure Web Site)都会使用到.本文会介绍一种在Win8.1和Win2012 R2上使用PowerShell快速生成 ...
- 《网络管理》IP地址管理与子网划分
IP地址管理——ipmaster ipmaster是一款对IP地址进行管理的软件,使用该软件可以提高网络管理员的工作效率.在大型网络中,使用该软件可以有序且高效地实现大中小型企业网IP地址的分配和管理 ...
- golang tar gzip 压缩,解压(含目录文件)
tar是用于文件归档,gzip用于压缩.仅仅用tar的话,达不到压缩的目的.我们常见的tar.gz就是用gzip压缩生成的tar归档文件. go实现tar压缩与解压与zip类似,区别在于tar需要使用 ...
- swift VS NSObject
Any class that does not inherit from another class is known as a base class. Swift classes do not in ...
- 【前端路由】Vue-router 中hash模式和history模式的区别
咱们今天说说VUE路由的hash模式与history模式的区别,这个也是面试常问的问题,不要小看这道题其实问到这里的时候那个面试官应该是个大牛,开发经验丰富,这个题其实就是考验你的开发经验是否属实. ...
- CAD把控件图形变成二进制流输出(com接口VB语言)
主要用到函数说明: _DMxDrawX::WriteBinStream 把控件图形变成二进制流输出,详细说明如下: 参数 说明 VARIANT* pVarBinArray 输出二进制数组.是个byte ...