-> "include/bits/allocator.h" -> "include/i386-redhat-linux/bits/c++allocator.h" -> "include/ext/new_allocator.h"(即是说,std::allocator == __gnu_cxx::new_allocator)
根据C++的标准,STL的allocator,把对象的申请和释放分成了4步:
第1步:申请内存空间,对应函数是allocator::allocate() 第2步:执行构造函数,对应函数是allocator::construct() 第3步:执行析构函数,对应函数是allocator::destroy() 第4步:释放内存空间,对应函数是allocator::deallocate() STL崇尚拷贝,你往容器里放东西或者从容器里取东西,都是要调用拷贝构造函数的。比如,你有一个对象a要插入到map里,过程是这样的:
map先申请一个结点的空间 调用拷贝构造函数初始化该结点 把新结点插入到map的红黑树中
STL中实现了好多种不同的更为具体的allocator,如下( GNU GCC关于Memory的官方文档 ):
__gnu_cxx::new_allocator: 简单地封装了new和delete操作符,通常就是std::allocator __gnu_cxx::malloc_allocator: 简单地封装了malloc和free函数 __gnu_cxx::array_allocator: 申请一堆内存 __gnu_cxx::debug_allocator: 用于debug __gnu_cxx::throw_allocator: 用于异常 __gnu_cxx::__pool_alloc: 基于内存池 __gnu_cxx::__mt_alloc: 对多线程环境进行了优化 __gnu_cxx::bitmap_allocator: keep track of the used and unused memory locations. 上面的8个allocator的实现中,bitmap_allocator、pool_allocator和__mt_alloc是基于cache的,其它的不基于cache
* 那么?如何指定使用一个特殊的allocator呢?示例如下:
map<int, int> a1; // 方法1
map<int, int, less<int>, std::allocator<pair<int, int> > > a3; // 方法2
// 方法3,方法1、方法2、方法3都是等价的map<int, int, less<int>, __gnu_cxx::new_allocator<pair<int, int> > > a2;
// 方法4,使用了基于cache的allocatormap<int, int, less<int>, __gnu_cxx::__pool_alloc<pair<int, int> > > a4;
内存碎片是容易被忽视的导致OutOfMemory的原因
这个观点有点类似于磁盘碎片,也可以称为内存碎片吧,当内存碎片过多的时候,极容易出现OutOfMemory错误;
使用STL的map特别容易出现这种情况,往map里插入了海量的小对象,然后释放了一些,然后再想申请内存时,就出现OutOfMemory错误了;
这种现象不只是在使用STL的情况会发现,下面举一个例子来说明内存碎片的问题,尽管这个例子没有使用STL。
举例之前,先说明一下这个例子中使用的两个查看当前进程的内存统计量的2个函数:
int get_max_malloc_length_inMB() : 得到当前可以申请的最长的内存长度(MB);这个函数不停地调用p=malloc(length*1024*1024);如果成功,则length++,并且free(p);如果失败,返回(length-1)。int get_free_mem_inKB() : 得到当前可以申请的内存总量(KB);这个函数不停地调用malloc(1024)来申请1KB的内存;如果成功,把这1KB的内存存起来,并且count++;如果失败,则把所有的1KB内存释放,再返回count。为了测试方便,我在运行程序前,设置了进程的最大内存为200MB,使用的命令如下:
ulimit -m 204800;
ulimit -v 204800;
这个例子把申请到的内存以矩阵的形式存储起来,先按列优先把指针存起来,再按行优先进行free,这样会造成大量的内存碎片;例子的伪代码如下:
typedef char* PtrType;
PtrType ** Ptrs = (PtrType**) malloc( ROW * sizeof(PtrType*) );
...
// 第1步: 占领所有的内存,按列优先进行申请
for(j=0; j<COL; ++j) {
for(i=0; i<ROW; ++i) {
Ptrs[j][i] = malloc(1024);
}
}
// 第2步:按行优先释放所有的内存,在中间多次调用get_max_malloc_length_inMB和get_free_mem_inKB来查看内存使用情况
for (i=0; i<ROW; ++i) {
for (j=0; j<COL; ++j) {
free( Ptrs[i][j] );
}
free(Ptrs[i]);
// 得到两个关于内存的统计量
get_max_malloc_length_inMB();
get_free_mem_inKB();
}
// 第3步:释放Ptrs,再获取一次内存的统计量
free(Ptrs);
get_max_malloc_length_inMB();
get_free_mem_inKB();
需要关注的是,内存的申请的顺序是按列优先的,而释放的顺序是按行优先的,这种做法就是模拟内存的碎片。<BR>
运行上面的程序后,得到的结果是:在释放内存的过程中,max_malloc_length_inMB长期保持在0 MB,当全部释放完后,max_malloc_length_inMB变成了 193 MB<BR>
max_malloc_length_inMB:
196 MB -> 0 MB -> 0 MB -> ... -> 0 MB -> 0 MB -> ...
-> 0 MB -> 0 MB -> 195 MB
free_mem_inKB:
199374 KB -> 528 KB -> 826 KB -> ... -> 96037 KB -> 96424 KB -> ...
-> 197828 KB -> 198215 KB -> 198730 KB
上面的结果引申出这样的结论:
OutOfMemory错误,并不一定是内存使用得太多;当一个程序申请了大量的小内存块 (比如往std::map中插入海量的小对象),导致内存碎片过多的话,一样有可能出现OutOfMemory错误