骄傲の猫的专栏

管理一群程序员,就象放牧一群骄傲的猫。

用户操作
[即时聊天] [发私信] [加为好友]
顾建华ID:ShardowM
3196次访问,排名2万外好友2人,关注者5
编程
ShardowM的文章
原创 5 篇
翻译 0 篇
转载 1 篇
评论 5 篇
最近评论
sky04:刚好在找这个方面资料,谢谢了
henry:现在比较清楚你的意思是什么了。下面有一些讨论:

--“花费一定的时间来构建链表”是指内存池在分配到一块大的内存后,需要在这块内存中建立链表,链接链表中各个节点的关系。

这个时间只是在构建 pool 时一次花销的。比起大量反复分配/释放小物件内存的实际客户程序的实际应该不算什么。

-- 我说的很大困难是指创建一个通用的内存池。
<……
骄傲的猫:把allocate改成下面那个样子,应该更合理
void* Pool::allocate(size_t size)
{
if (size != _block)
return ::operator new(size);

//在被stackPtr堆栈中获得内存的地址
if (top > 0) ……
骄傲的猫:我说的很大困难是指创建一个通用的内存池。在Scott Meyers的Effective C++中介绍的内存池,是通过在被创建的对象中加一个Next指针使其指向下一个对象来建立链表的,这样的话内存池在给对象分配内存时必须通过Next指针来知道下一个对象的地址,因此内存池类就必须知道对象的类的接口,因此也无法将内存池类和对象的类独立出来。
“花费一定的时间来构建链表”是指内存池在分配到一……
henry:-- 但是正是由于链表,给创建一个可通用的内存池类带来了很大的困难,而且在程序必须花费一定的时间来构建链表。

这句话很费解。操作链表很容易,怎么会有什么很大困难?楼主的很大困难具体指的是什么?另外,“花费一定的时间来构建链表”又具体指什么?
文章分类
收藏
    相册
    朋友
    jewely的Blog(RSS)
    Mailbomb的J2ME专栏(RSS)
    英文技术网站
    Code Guru
    Code Project
    NDSTech
    Planet Source Code
    Programmers Heavern
    SGI
    中文技术网站
    CSDN_GameDev的专栏(RSS)
    Winmag国际中文版(简体版)
    中国协议分析网
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 简单内存池(Memory Pool)类的实现收藏

    新一篇: ASP.net动态调用非托管DLL的参数传递问题 | 

    简单内存池(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

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

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

    发表于 @ 2006年09月15日 20:00:00|评论(loading...)|编辑

    新一篇: ASP.net动态调用非托管DLL的参数传递问题 | 

    评论

    #henry 发表于2006-09-17 07:32:00  IP: 24.193.79.*
    -- 但是正是由于链表,给创建一个可通用的内存池类带来了很大的困难,而且在程序必须花费一定的时间来构建链表。

    这句话很费解。操作链表很容易,怎么会有什么很大困难?楼主的很大困难具体指的是什么?另外,“花费一定的时间来构建链表”又具体指什么?
    #骄傲的猫 发表于2006-09-17 23:04:00  IP: 61.173.70.*
    我说的很大困难是指创建一个通用的内存池。在Scott Meyers的Effective C++中介绍的内存池,是通过在被创建的对象中加一个Next指针使其指向下一个对象来建立链表的,这样的话内存池在给对象分配内存时必须通过Next指针来知道下一个对象的地址,因此内存池类就必须知道对象的类的接口,因此也无法将内存池类和对象的类独立出来。
    “花费一定的时间来构建链表”是指内存池在分配到一块大的内存后,需要在这块内存中建立链表,链接链表中各个节点的关系。
    #骄傲的猫 发表于2006-09-17 23:07:00  IP: 61.173.70.*
    把allocate改成下面那个样子,应该更合理
    void* Pool::allocate(size_t size)
    {
    if (size != _block)
    return ::operator new(size);

    //在被stackPtr堆栈中获得内存的地址
    if (top > 0)
    {
    return stackPtr[--top];
    }

    //在大内存中分配内存
    if (curr < limit)
    {
    void* ptr = curr;
    curr = static_cast<unsigned char*>(curr) + size;
    return ptr;
    }

    return NULL;
    }
    #henry 发表于2006-09-18 12:16:00  IP: 24.193.79.*
    现在比较清楚你的意思是什么了。下面有一些讨论:

    --“花费一定的时间来构建链表”是指内存池在分配到一块大的内存后,需要在这块内存中建立链表,链接链表中各个节点的关系。

    这个时间只是在构建 pool 时一次花销的。比起大量反复分配/释放小物件内存的实际客户程序的实际应该不算什么。

    -- 我说的很大困难是指创建一个通用的内存池。

    用链表实现通用的内存池不一定就非得遇到很大困难(参看下文)。

    -- 在Scott Meyers的Effective C++中介绍的内存池,是通过在被创建的对象中加一个Next指针使其指向下一个对象来建立链表的,这样的话内存池在给对象分配内存时必须通过Next指针来知道下一个对象的地址,因此内存池类就必须知道对象的类的接口,因此也无法将内存池类和对象的类独立出来。

    没仔细看过Scott的实现,不过听上去这是个很弱实现。
    首先,内存池没有必要知道所分配的内存将用于创建什么类的对象。内存池只要按所要求的大小把内存分配/释放就算完美。任何多余行为都是坏的设计。
    其次,“被创建的对象中加一个Next指针”这种的实现实在让人不可理解。怎么设计不好,为什么非要设计成侵入式的?这种设计, 1. 要求pool和物件的偶联,不可能产生通用pool,而且2. 操作起来确实有点复杂。
    第三,链表实现pool其实很简单,甚至可以比你的用栈的实现还简单。链表实现的好处是没有很大的额外内存开销。你的实现则需要在pool memory外,为那个栈花费相当的内存。
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © 骄傲的猫