一般我们习惯的c++内存配置如下

class Foo { ... };
Foo* pf = new Foo;
delete pf;

这里的new实际上分为两部分执行。首先是先用::operator new配置内存,然后执行Foo::Foo()构造对象内容。delete也一样,先运行Foo::~Foo()析构对象,再用::operator delete释放内存。在SGI STL中,这两部分分别在<stl_alloc.h>和<stl_construct.h>中。本文讲的便是<stl_alloc.h>中的故事。
  SGI STL中将配置器分为两级。第一级直接用malloc和free管理内存,第二级则使用内存池以避免内存碎片。这两级都由simple_alloc包装起来以符合stl标准。如图

第一级由于没有用operator new,所以要自己实现new-handler机制。我仿写的代码如下

 #ifndef _MALLOC_ALLOC_H_
#define _MALLOC_ALLOC_H_ //定义内存不足又没有定义相关处理函数时抛出的异常
#ifndef THROW_OOM
# include <stdio.h>
# include <stdlib.h>
# define THROW_OOM fprintf(stderr, "out of memory\n"); exit()
#endif #include<stdlib.h> namespace Chenstl{ //第一级空间配置器,直接用mallloc分配内存
//当需要分配的空间大于MAX_BYTES时使用
class malloc_alloc{
private:
static void *oom_malloc(size_t); //声明时可以只写类型啊。。现在才知道
static void *oom_realloc(void *,size_t);
static void (* malloc_oom_handler)(); //处理malloc时内存不足时的函数指针
public:
static void *allocate(size_t n);
static void decllocate(void *p); static void *realloc(void *p, size_t new_sz);
//当内存不足时,需要客户端设置handler
static void set_malloc_oom_handler(void(*f)());
};
} #endif
 #include "malloc_alloc.h"

 using namespace Chenstl;
void *malloc_alloc::allocate(size_t n)
{
void *result = malloc(n);
if ( == result) result = oom_malloc(n);
return result;
} void malloc_alloc::decllocate(void *p)
{
free(p);
} void * malloc_alloc::realloc(void *p, size_t new_sz)
{
void *result = realloc(p, new_sz);
if ( == result) result = oom_realloc(p, new_sz);
return result;
} //当内存不足时,需要客户端设置handler
void malloc_alloc::set_malloc_oom_handler(void(*f)())
{
malloc_oom_handler = f;
} void(*malloc_alloc::malloc_oom_handler)() = ; void *malloc_alloc::oom_malloc(size_t n)
{//不断试图获得内存
void *result;
for (;;) //据说这样比while(1)效果更优
{
if ( == malloc_oom_handler) THROW_OOM;
(*malloc_oom_handler)();
result = malloc(n);
if (result) return result;
}
} void *malloc_alloc::oom_realloc(void *p, size_t n)
{
void *result;
for (;;)
{
if ( == malloc_oom_handler) THROW_OOM;
(*malloc_oom_handler)();
result = realloc(p, n);
if (result) return result;
}
}

malloc_alloc.cpp

如果需要的区块超过128bytes则用第一级,否则用第二级的内存池管理。为了便于管理,配置器会自动将内存需求量上调到8的倍数(要求20bytes时,自动调整为24bytes)。用16个freelist管理内存池,为节省空间,使用union

union obj {   //free-lists的节点构造 
   union obj *next;
   char client[1]; //使用者可见
  };

获取内存时的代码及步骤如下

void *default_alloc::allocate(size_t n)
{
obj *result = ;
obj **my_free_list = ;
if (n > MAX_BYTES)
return malloc_alloc::allocate(n);
//寻找free lists中合适的一个
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if( == result)
{//没有找到可用的freelist,从内存池里取出空间
return refill(ROUND_UP(n));
}
//调整freelist
*my_free_list = result->next;
return result;
}

当free list中没有可用区块时,调用refill()为free list填充空间,新的空间取自内存池(由chunk_alloc()完成)。如果内存池不够,则malloc之,如果系统heap空间也不够,chunk_alloc()就寻找还有空闲区块的free list并将其内存充公,如果还是不够就调用第一级配置器。第一级配置器有实现new-handler机制,内存不够会抛出异常。

 
#ifndef _DEFAULT_ALLOC_H
#define _DEFAULT_ALLOC_H namespace Chenstl{
//使用内存池以减少碎片
class default_alloc {
private:
enum { ALIGN = };
enum { MAX_BYTES = };
enum { NFREELISTS = };
//static const int ALIGN = 8;
//static const int MAX_BYTES = 128;
//static const int NFREELISTS = 16; //MAX_BYTES/ALIGN
union obj { //free-lists的节点构造
union obj *next;
char client[];
};
//freelist
static obj *free_list[NFREELISTS];
static char *start_free; //内存池的起始位置
static char *end_free; //内存池的终止位置
static size_t heap_size; private:
//将bytes上调至8的倍数
static size_t ROUND_UP(size_t bytes) {
return ((bytes +ALIGN - ) & ~(ALIGN - ));
}
//获取合适的区块在freelist中的位置
static size_t FREELIST_INDEX(size_t __bytes) {
return (((__bytes)+(size_t)ALIGN - ) / (size_t)ALIGN - );
}
//返回一个大小为n的对象,并可能加入大小为n的其他区块到free-list
static void *refill(size_t n);
//配置一大块空间,可容纳nobjs个大小为size的区块
//如果配置nobjs个区块有所不便,nobjs可能会降低
static char *chunk_alloc(size_t size, int &nobjs); public:
static void *allocate(size_t n);
static void deallocate(void *p, size_t n);
static void *realloc(void *p, size_t old_sz, size_t new_sz);
};
} #endif

default_alloc.h

#include "default_alloc.h"
#include "malloc_alloc.h"
using namespace Chenstl; default_alloc::obj *default_alloc::free_list[NFREELISTS]
= { , , , , , , , , , , , , , , , , };
char *default_alloc::start_free = ; //内存池的起始位置
char *default_alloc::end_free = ; //内存池的终止位置
size_t default_alloc::heap_size = ; void *default_alloc::allocate(size_t n)
{
obj *result = ;
obj **my_free_list = ;
if (n > MAX_BYTES)
return malloc_alloc::allocate(n);
//寻找free lists中合适的一个
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if( == result)
{//没有找到可用的freelist,从内存池里取出空间
return refill(ROUND_UP(n));
}
//调整freelist
*my_free_list = result->next;
return result;
} void default_alloc::deallocate(void *p, size_t n)
{ }
//返回一个大小为n的对象,并可能加入大小为n的其他区块到freelist
//在ANSI c中,void *不允许进行加减操作,所以chunk用char *
void *default_alloc::refill(size_t n)
{
int objs = ;
char *chunk = chunk_alloc(n, objs); obj *next, *current;
obj *result;
obj **my_free_list;
if ( == objs) //只取出一个区块
return chunk;
my_free_list = free_list + FREELIST_INDEX(n);
result = (obj *)chunk; //这一块返回给客户端
//将freellist指向分配的区域
*my_free_list = next = (obj *)chunk + n;
for (int i = ;; i++)
{
current = next;
next = (obj *)((char *)next + n); //这里注意不能直接用next+n
if (i == objs - )
{
current->next = ;
break;
}
else
current->next = next;
}
return result;
} char *default_alloc::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 = nobjs * size;
result = start_free;
start_free += total_bytes;
return result;
}
else
{//内存池一块区块也供应不了
size_t bytes_to_get = * total_bytes + ROUND_UP(heap_size >> );;
if (bytes_left>)
{//将内存池的零头分配给合适的freelist
obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
((obj *)start_free)->next = *my_free_list;
*my_free_list = (obj *)start_free;
}
start_free = (char *)malloc(bytes_to_get);
if (!start_free)
{//系统堆内存不足,寻找还未使用的freelist
obj *p = ;
obj **my_free_list = ;
for (int i = size; i < MAX_BYTES; ++i)
{
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if ( != p)
{//还有未使用的freelist
start_free = (char *)p;
*my_free_list = p->next;
end_free = start_free + i;
//递归调用,修正nobjs
return chunk_alloc(size, nobjs);
}
}
//没内存可用,寄希望于第一级的new-handler或抛出异常
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);//递归调用,修正nobjs
}
}

default_alloc.cpp

stl中的空间配置器的更多相关文章

  1. STL——模拟实现空间配置器

    目录 问题 SGI版本空间配置器-std::alloc 一级空间配置器 二级空间配置器 Refill.chunkAlloc函数 最后,配置器封装的simple_alloc接口 问题 我们在日常编写C+ ...

  2. C++ STL学习之 空间配置器(allocator)

    众所周知,一般情况下,一个程序包括数据结构和相应的算法,而数据结构作为存储数据的组织形式,与内存空间有着密切的联系. 在C++ STL中,空间配置器便是用来实现内存空间(一般是内存,也可以是硬盘等空间 ...

  3. stl第二级空间配置器详解(1)

    SGI STL考虑到小型内存区块的碎片问题,设计了双层级配置器,第一级配置直接使用malloc()和free():第二级配置器则视情况采用不同的策略,当配置区大于128bytes时,直接调用第一级配置 ...

  4. STL源码剖析之空间配置器

    本文大致对STL中的空间配置器进行一个简单的讲解,由于只是一篇博客类型的文章,无法将源码表现到面面俱到,所以真正感兴趣的码农们可以从源码中或者<STL源码剖析>仔细了解一下. 1,为什么S ...

  5. stl空间配置器线程安全问题补充

    摘要 在上一篇博客<STL空间配置器那点事>简单介绍了空间配置器的基本实现 两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等. 在最后,又关 ...

  6. 【转】STL空间配置器

    STL空间配置器(allocator)在所有容器内部默默工作,负责空间的配置和回收.STL标准为空间配置器定义了标准接口(可见<STL源码剖析>P43).而具体实现细节则由各编译器实现版本 ...

  7. 带你深入理解STL之空间配置器(思维导图+源码)

    前不久把STL细看了一遍,由于看得太"认真",忘了做笔记,归纳和总结这步漏掉了.于是为了加深印象,打算重看一遍,并记录下来里面的一些实现细节.方便以后能较好的复习它. 以前在项目中 ...

  8. stl空间配置器简介

    1. 符合STL标准的空间配器接口 STL是c++中使用非常广泛的一个标准库,它包含各种有用的容器.而空间配置器作为STL各种容器的背后的核心,负责容器内部内存的分配和释放.不过空间配置器可以分配的也 ...

  9. STL空间配置器解析和实现

    STL空间配置器的强大和借鉴作用不言而喻,查阅资料,发现了Dawn_sf已经对其有了极其深入和详细的描述,所以决定偷下懒借用其内容,只提供自己实现STL空间配置器的源码,具体解析内容参考:(一)STL ...

随机推荐

  1. xpath 操作XML

    1.xpath 操作XML,底下部分代码被注释了,但是是完整功能,去除注释是正常使用的(有写命名和其他冲突,所以注释了) 总体有:完整读取xml,对xml的增删改查,对xml的特定操作 using S ...

  2. java web(jsp)-The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path

    在静态项目上新建 jsp文件的时候,报错:The superclass "javax.servlet.http.HttpServlet" was not found on the ...

  3. 层模型--固定定位(position:fixed)

    fixed:表示固定定位,与absolute定位类型类似,但它的相对移动的坐标是视图(屏幕内的网页窗口)本身. 由于视图本身是固定的,它不会随浏览器窗口的滚动条滚动而变化,除非你在屏幕中移动浏览器窗口 ...

  4. java_设计模式_命令模式_Command Pattern(2016-08-09)

    理解还不到位,先窜出来.等过一阵子再看,再理解. 定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 类型:行为类模式 类 ...

  5. c读写文件相关

    1.打开文件: 函数原型: FILE * fopen(const char * path,const char * mode); 返回值: 文件顺利打开后,指向该流的文件指针就会被返回.如果文件打开失 ...

  6. 安装freebsd9 出现 mountroot>怎么办

    之前手欠把linux分区给删了想重装freebsd 重新进入的时候mbr提示grub信息 用PE把MBR删掉 之后再用freebsd光盘启动出现mountroot> 就用mountroot> ...

  7. IE6中的常见BUG与相应的解决办法

    开发前端的同学一定都知道,IE6是兼容BUG最多的浏览器,它不支持PNG alpha通道暂且不论.其文档的解析理解规范也引起了诸多恼人的BUG,有时甚至让人感到绝望.本文主要讲解一些比较容易遇到的IE ...

  8. opencv数据结构总结

    OpenCV里面用到了很多图像相关的数据结构,熟练掌握它们是学习图像的基础. 1.IplImage IplImage IplImage IPL 图像头 typedef struct _IplImage ...

  9. xml格式化写入文件

    参考xml文件地址:http://cloudprint.cainiao.com/template/standard/101 代码: package main import ( "encodi ...

  10. C#中&与&&的区别

    刚刚翻书发现这个问题,在网上找了一下,我的理解吧. 他俩的区别就是“&”和“|”不执行短路计算,而&&和||执行了短路计算. &不执行短路计算 ——————表达式A&a ...