简单内存池(Memory Pool)类的实现

来源:互联网 发布:ac尼尔森数据分析 编辑:程序博客网 时间:2024/05/21 17:04

简单内存池(Memory Pool)类的实现

作者:骄傲的猫

出处:http://blog.csdn.net/shardowm

一般的内存池的实现都是在申请一块大内存后,在这块内存中构造一个欲创建的对象的链表,然后通过对链表的管理实现对象内存的分配与回收。由于程序可以非常便捷的操作链表,所以这种方式实现的内存池可以大大提高程序分配和回收内存的效率。但是正是由于链表,给创建一个可通用的内存池类带来了很大的困难,而且在程序必须花费一定的时间来构建链表。解决这两个问题的关键是链表,那么是否可以在内存池中避免使用链表呢?答案是肯定的。

 下面的代码就是一个不使用链表的内存池类的声明。 

#pragma once


#include
<new>
using namespace std;

class Pool
{
public:
    
explicit Pool(size_t block_size);
    
~Pool(void);
    
void* allocate(size_t size);                     //分配内存函数
    void free(void* p, size_t n);                    //回收内存函数
private:
    size_t    _block;                                   
//被分配的内存块的大小
    static const int MAX_BLOCKS = 1024;              //可创建对象数
    void*    stackPtr[MAX_BLOCKS];                      //存放废弃的内存块地址的数组    
   int           top;
    
void*     base;                                      //base、limit保存大内存块地址范围
    void*     limit;
    
void*     curr;                                      //内存块中可分配内存的首地址
};

以下类的构造函数

Pool::Pool(size_t block_size)
: top(
0)
base(NULL)
, limit(NULL)
{
    
base = ::operator new(block_size * MAX_BLOCKS);
    limit 
= static_cast<unsigned char*>(base+ block_size * MAX_BLOCKS;
    curr 
= base;
    _block 
= block_size;
}

 在构造函数中我们申请了一块连续的大内存,并将它的地址的范围保存在base和limit两个指针中,并将该内存的起始地址赋给了curr指针。

为了便于理解Pool类,让我们先来看看负责回收内存的free函数是如何实现的。

void Pool::free(void* p, size_t n)
{
    
if (n != _block)
        ::
operator delete(p);
    
else
        stackPtr[top
++= p;           //将欲回收的内存的地址压入statckPtr堆栈
    return;
}

在这个函数中,stackPtr和top实现了一个堆栈的功能,我们是将被回收的内存地址保存到了这个堆栈的末尾,而不像一般的内存池一样将被回收的内存插入到队列的最前面。

下面是分配内存的allocate函数


void* Pool::allocate(size_t size)
{
    
if (size != _block)
        
return ::operator new(size);

    
//在大内存中分配内存
    if (curr < limit)
    {
        
void* ptr = curr;
        curr 
= static_cast<unsigned char*>(curr) + size;
        
return ptr;
    }
    
    
//在被stackPtr堆栈中获得内存的地址
    if (top > 0)
    {
        
return stackPtr[--top];
    }
    
return NULL;
}

在该函数中我们处理了两种可能出现的内存分配情况。第一种,直接在被分配的大内存中分配内存。我们说过在构造Pool类对象时我们将分配的大内存的起始地址赋给了curr指针,其实该指针时指向大内存中未被分配的内存的起始地址,通过返回curr获得分配的内存的地址,然后我们在curr上加上被分配内存的大小,使curr指向下一块将要被分配的内存的地址,直到curr=limit。第二种方式是在stackPtr堆栈中获得内存的地址,在讲free函数是我们说过stackPtr保存了被回收的内存地址,所以我们可以直接从堆栈上获取分配的内存的地址。

最后对我们实现内存池、普通的内存池和不使用内存池的情况进行测试,连续创建、释放一个只有一个int型数据成员的类的对象一亿次:这个内存池所需时间平均约:2375ms,普通的内存池平均约:2535ms,不使用内存池约:21100ms

测试机器:CPU: 讯驰II 2.0G,  内存:1G DDR533

-----------------------------------------------

由于写作经验有限难免在表达上有不清楚的地方和疏忽的地方(写文章向来是我的弱项,呵呵),还望大家见谅。同时热烈欢迎各位网友提出宝贵意见。

原创粉丝点击