这几天在研究stl的内存配置器,作用是防止零散的申请内存块导致过多的内存碎片。

大体思路是:

维护一个freelist, 一个内存块链表,就是一个链表,链表上的每一个节点都一个指针指向一块内存块,如果有申请内存,就直接将此链表上的一个内存块分配出去;

如果在链表上找不到合适的内存块,或者说大小为n的内存块已经被分配完了,这时就需要从内存池中拿到一大块内存,然后再将这大块内存按照大小为n进行连接成链表再放入freelist中,显然内存池中有一块很大的内存块,这个大内存块已经分配好了。

但是如果内存池中的内存块也用完了,这时才需要用malloc再申请一块大内存块。

一般情况下我们构建单链表时需要创建如下的一个结构体。

struct Obj
{
    Obj *next;
    Char* p;
    Int iSize;
}

next指针指向下一个这样的结构,p指向真正可用空间,iSize用于只是可用空间的大小,在其他的一些内存池实现中,还有更复杂的结构体,比如还包括记录此结构体的上级结构体的指针,结构体中当前使用空间的变量等,当用户申请空间时,把此结构体添加的用户申请空间中去,比如用户申请12字节的空间,可以这样做

Obj *p = (Obj*)malloc(12+sizeof(Obj));
p->next = NULL;
p->p = (char*)p+sizeof(Obj);
p->iSize = 12;

但是,我们并没有采用这种方式,这种方式的一个缺点就是,用户申请小空间时,内存池加料太多了。比如用户申请12字节时,而真实情况是内存池向内存申请了12+ sizeof(Obj)=12+12=24字节的内存空间,这样浪费大量内存用在标记内存空间上去,并且也没有体现索引表的优势

如图所示:

红色为结点,蓝色为结点指向的内存块

在sgi中是这样定义的:

union Obj
{
    Obj *next;
    char client_data[1];
}

next指向下一个内存块,这个内存块最开始的部分放有next, 当将这个内存块给客户时,也把next所占的内存给客户,也就是说这个链表不占用额外的内存空间。

如图所示:

途中一共四个内存块,next/client_data就在各自的内存块中。由于union的特性,如果看next,这个变量指向的是下一块内存块的地址;如果看client_data,就可以将其看做柔性数组进行理解,client_data是个指针,指向的是其所在内存块的位置,就是讲client_data看做一个数组。

个人认为,client_data完全没有用途,sgi只所以还保留client_data只是为了便于理解。

所以在我个人实现的内存配置器,就省略了client_data

还有一个问题:

为什么在泛型中用到了template<int inst>, 而在之后的实现中却没有用到inst?

至于inst应该是为了生成不同的实例,在多线程环境下可以提高速度。举个例子:比如你有两个线程,A调用allocate分配8个字节,正在把这块内存从free_list取下是时,系统调度将A停下B开始执行,恰好B也要分配8个字节,于是把分配给A的抢了过来(因为free_list没有更新,而且整个allocate的数据都是静态的,被整个class共享),A接着运行,他并不知道B已经拿走了这块内存,结果同一块内存同一时间分给了两个线程,错误产生了。要避免这种错误就必须加锁,细节你可以看STL原码,速度自然变慢。解决这种问题的办法就是利用inst,为不同的线程指定不同的inst生成不同的静态memeber data。不同的线程有不同的free_list,自然就不用加锁了!

#ifndef C_ALLOC_H
#define C_ALLOC_H
#include <stdio.h>
#include <stdlib.h>enum {ALIGN = 8};//
enum {MAX_BYTES = 128};
enum {NFREELISTS = 16};#define __THROW_BAD_ALLOC std::cerr << "out of memory " <<endl; exit(1)
//第一级配置器
template <int inst>//这个模板参数在单线程中没有用,主要用于多线程。__malloc_alloc_template<0>,__malloc_alloc_template<1>就实例化出两个不同的类,可以用于两个不同的线程中,这样既不用加锁也不会减速
class __malloc_alloc_template {
private://oom: out of memorystatic void * oom_malloc( size_t);static void * oom_realloc(void *, size_t);static void (* __malloc_alloc_oom_handler )();//这是个函数指针,是一个成员变量,而不是成员函数public:static void * allocate (size_t n) {void * result = malloc(n);if (0 == result) result == oom_malloc(n);return result;}static void deallocate(void *p, size_t) {free(p);}static void *reallocate(void *p, size_t /*old size*/, size_t new_sz) {void *result = realloc(p,new_sz);if (0 == result) return = oom_realloc(p, new_sz);return result;}static void (* set_malloc_handler (void (*f)())) () { //set_malloc_handler是一个函数,其参数是一个函数指针,其返回值也是一个函数指针。这地方要好好揣摩。如果将set_malloc_handler (void (*f)()) 看做p,则就是 (*p)(),set_malloc_handler的返回值就是pvoid (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler == f;return old;}};template<int inst>
void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler) () = 0;template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(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 = malloc(n);//再重新尝试配置内存if (result) return result;}
}template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t) {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;}
}typedef __malloc_alloc_template<0> malloc_alloc;
//第二级配置器template <bool threads, int inst>
class __default_alloc_template {
private://bytes上调至8的倍数static size_t ROUND_UP(size_t bytes) {return ( (bytes + ALIGN -1) & ~(ALIGN - 1));}private:union obj {union obj * free_list_link;};
private:static obj * free_list[NFREELISTS];static size_t FREELIST_INDEX(size_t bytes) {return ( (bytes + ALIGN -1)/ALIGN -1);}//当freelist中没有大小为n个块,调用此函数,会返回从内存池中返回若干个块,将其中的一个返回,将剩余的放入freelist中static void *refill(size_t n);//从内存池中分配一大块空间,大小为nobjs个大小为 size的块,如果内存不足,nobjs会减小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) {obj ** my_free_list;obj * result;if (n > MAX_BYTES) return (malloc_alloc::allocate(n));my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;if (result == 0) {void *r = refill(ROUND_UP(n));return r;}*my_free_list = result->free_list_link;return result;}static void deallocate(void *p, size_t n) {obj * q = (obj *) p;obj ** my_free_list;if (n >MAX_BYTES) {//对于大块就free,对于小块是要回收到freelist中,以备再次使用malloc_alloc::deallocate(p,n);return;}my_free_list = free_list + FREELIST_INDEX(n);q->free_list_link = *my_free_list;my_free_list->free_list_link = q;}static void * reallocate(void *p, size_t old_sz, size_t new_sz) {void * result;size_t copy_sz;if (old_sz > MAX_BYTES && new_sz > MAX_BYTES) {return (malloc_alloc::reallocate(p,old_sz, new_sz));}if (ROUND_UP(old_sz) == 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;}
};template<bool threads, int inst>
void * __default_alloc_template<threads,inst>::refill(size_t n) {int nobjs = 20;char *chunk = chunk_alloc(n, nobjs);obj ** my_free_list;obj * result;obj * current_obj, * next_obj;int i;if (1 == nobjs) return chunk;my_free_list = free_list + FREELIST_INDEX(n);result = (obj *)chunk;*my_free_list = next_obj = (obj *)(chunk + n);for (int i = 1;; ++i) {current_obj = next_obj;next_obj = (obj *)((char *)next_obj + n);if (i == nobjs - 1) {current_obj->free_list_link = NULL;break;}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){//至少能提供一个块result = start_free;nobjs = bytes_left / size;total_bytes = size * nobjs;start_free += total_bytes;return result;}else {size_t bytes_to_get = 2 * total_bytes +ROUND_UP(heap_size >> 4);//ROUND_UP(heap_size >> 4)作用不大if (bytes_left >0) {obj ** 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 (0 == start_free) {//没有多余内存,需要从freelist中找到块int i;obj ** my_free_list, *p;for (i = size; i < MAX_BYTES; i += ALIGN) {my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;if (0 != p) {*my_free_list = p->free_list_link;start_free = (char *)p;end_free = start_free + i;return chunk_alloc(size,nobjs);}}end_free = 0;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);}}
}template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::start_free = 0;template<bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = 0;template<bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;//注意一定要有typename告诉编译器,这个模板类肯定有这个类型obj
template<bool threads, int inst>
typename __default_alloc_template<threads, inst>::obj *
__default_alloc_template<threads, inst>::free_list[NFREELISTS] =
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };typedef __default_alloc_template<false, 0> alloc;template<class T, class Alloc>
class simple_alloc {
public://返回n个T大小的内存static T *allocate(size_t n) {return 0 == N ? 0 : (T *) Alloc::allocate(N * sizeof(T));}static T *allocate() {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));}
};#endif

之下开始研究vector

参考:http://blog.csdn.net/yangzhongxuan/article/details/8017629

sgi allocate相关推荐

  1. malloc开辟的空间在哪一个区间_C++进阶系列之STL(2)SGI版本空间配置器

    1.STL中的空间配置器在STL中,空间配置器分了2组,分别为一级空间配置器和二级空间配置器,但是它们都有自己各自运用的场合:一般说来,一级空间配置器一般分配的空间大于128B,二级空间配置器的分配空 ...

  2. SGI STL 内存分配方式及malloc底层实现分析

    在STL中考虑到小型区块所可能造成的内存碎片问题,SGI STL设计了双层级配置器,第一级配置器直接使用malloc()和free();第二级配置器则视情况采用不同的策略:当配置区块超过128byte ...

  3. sgi stl 之list

    sgi stl 的list实际上是一个双向环形链表 下面是代码 #ifndef C_LIST_H #define C_LIST_H #include "cconstruct.h" ...

  4. sgi 之vector

    最简单的sgi vector竟然写了四五天. 这次编写所暴露的问题是: 1. 一定要单元测试,否则在最后差错的时候会崩溃的 2. 写代码一定要仔细,记住,要bugfree ccconstruct.h ...

  5. SGI STL 学习笔记二 vector

    sequence containers Array Vector Heap Priority_queue List sList(not in standard) Deque Stack Queue S ...

  6. 详解STL中的空间配置器(SGI版本)

    空间配置器 1.什么是空间配置器 为各个容器高效的管理空间(空间的申请与回收)的 2.为什么需要空间配置器 各种容器----->可以存放元素---->底层需要空间 new 申请空间 ope ...

  7. 【STL深入学习】SGI STL空间配置器详解(二)-第二级空间配置器

    本文讲解SGI STL空间配置器的第二级配置器. 相比第一级配置器,第二级配置器多了一些机制,避免小额区块造成内存的碎片.不仅仅是碎片的问题,配置时的额外负担也是一个大问题.因为区块越小,额外负担所占 ...

  8. 【STL深入学习】SGI STL空间配置器详解(一)-第一级空间配置器

    一.SGI STL配置器简介 SGI STL的配置器与众不同,它与标准规范不同.如果要在程序中明确使用SGI配置器,那么应该这样写: vector<int,std::alloc> iv; ...

  9. SGI版本空间配置器

    1.STL中的空间配置器 在STL中,空间配置器分了2组,分别为一级空间配置器和二级空间配置器,但是它们都有自己各自运用的场合:一般说来,一级空间配置器一般分配的空间大于128B,二级空间配置器的分配 ...

最新文章

  1. 复位 stm32_stm32学习笔记
  2. Windows内核的表学习总结
  3. java 匹配mysql按钮_使用Java在mysql查询中设置匹配函数
  4. 计算机科学与技术大学生职业规划,计算机科学与技术大学生职业生涯规划ppt
  5. STM32工作笔记0047--认识DTU什么是4GDTU设备
  6. ASP.NET 4.0尚未在 Web 服务器上注册 解决方法
  7. idea将项目上传到SVN
  8. 平面设计banner排版技巧哪些比较实用
  9. Redis下载与安装 Linux + Windows 较详细步骤
  10. winhex数据恢复linux,winhex数据恢复完整图文教程
  11. Matlab运算符总结
  12. 怎样使用计算机定时关机,win10怎么定时关机?
  13. 计算机usb无法使用,电脑USB接口都不能用的解决办法[多图]
  14. 图片如何转为GIF?gif格式的图片怎么做?
  15. 海思SD3403/SS928开发(三)红外DC接入
  16. mapbox样式规范(style)
  17. 花12个月做成功网站
  18. 牛客 换钱的最少货币数
  19. 学生报名太火热,黑马大门要被挤掉了?
  20. 国内外免费的建站程序汇总(收藏)

热门文章

  1. 十、深入Java字符串(下篇)
  2. 九、爬虫学会如何入库
  3. java string问题_Java关于String的问题?
  4. 微信小程序之授权登录--项目需要
  5. SimBERTv2来了!融合检索和生成的RoFormer-Sim模型
  6. 为什么大家都在吹捧Python?
  7. MySQL学习第三章练习题
  8. 数学中的向量乘积和矩阵乘积总结
  9. matlab求系统根轨迹代码_根轨迹法、PID参数整定和matlab指令计算
  10. Hasor【付诸实践 01】低代码框架 DataQL 聚合查询引擎 SQL执行器报错 Query dialect missing 原因分析及解决(针对GreenPlum数据库)