注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

暇隙读书(12)—由一个简单的内存池展开之二可变大小内存池  

2015-05-18 16:32:52|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

由一个简单的内存池展开之二可变大小内存池

上一篇博文里处理了一个固定大小的内存池,这样的内存池是最常见也是最常用的。但是在Web服务器的编写中一般会遇到不同的Http的请求,这些请求大多数据量很小但是却非常多,这样的内存分配最容易出现内存碎片,所以说,内存池如果能够满足这种需求是最棒的。今天就看看《提高C++的编程性能的编程技术》里的第六章中关于单线程可变大小内存管理器的用法。先看一个简单的代码:

说明:代码来自网上,http://www.cnblogs.com/kex1n/archive/2011/08/16/2140376.html,非常感谢之。做了细小的修改。

// TestMemPoolEx.cpp :

//

 

#include "stdafx.h"

 

#include "stdafx.h"

#include <windows.h>

#include <MMSystem.h>

#include <iostream>

using namespace std;

 

#pragma comment(lib, "winmm.lib")

 

class Foo

{

public:

    Foo(int a = 0, int b = 0):m(a), n(b) {}

 

private:

    int m;

    int n;

};

 

class MemoryChunk

{

public:

    MemoryChunk(MemoryChunk* next, size_t reqSize)

    {

        m_pNext = next;

        size_t chunkSize = reqSize > DEFAULT_CHUNK_SIZE ? reqSize : DEFAULT_CHUNK_SIZE;

        m_nChunkSize = chunkSize;

        m_pMem = new char[m_nChunkSize];

        m_nBytesAlreadyAllocated = 0;

    }

 

    ~MemoryChunk() {delete m_pMem;}

 

    void* alloc(size_t size)

    {

        void* pAddr =  (void*)((size_t)m_pMem + m_nBytesAlreadyAllocated);

        m_nBytesAlreadyAllocated += size;

        return pAddr;

    }

 

    void  free(void* doomed)

    {

        //do nothing

        //

    }

 

    MemoryChunk* NextMemoryChunk() const {return m_pNext;}

 

    size_t SpaceAvailiable() const {return m_nChunkSize - m_nBytesAlreadyAllocated;}

 

    enum{DEFAULT_CHUNK_SIZE = 4096};

 

private:

    MemoryChunk* m_pNext;

    void* m_pMem;

    size_t m_nChunkSize;

    size_t m_nBytesAlreadyAllocated;

};

 

class ByteMemoryPool

{

public:

    ByteMemoryPool(size_t initSize = MemoryChunk::DEFAULT_CHUNK_SIZE)

    {

        m_chunkList = 0; //must set to null

        ExpandStorage(initSize);

    }

 

    ~ByteMemoryPool()

    {

        MemoryChunk* pChunk = m_chunkList;

        while (pChunk)

        {

            m_chunkList = pChunk->NextMemoryChunk();

            delete pChunk;

            pChunk = m_chunkList;

        }

    }

 

    void* alloc(size_t reqSize)

    {

        if (reqSize > m_chunkList->SpaceAvailiable())

        {

            ExpandStorage(reqSize);

        }

 

        return m_chunkList->alloc(reqSize);

    }

 

    void free(void* doomed)

    {

        m_chunkList->free(doomed);

    }

 

private:

    MemoryChunk* m_chunkList;

 

    void ExpandStorage(size_t reqSize)

    {

        MemoryChunk* pTmp = new MemoryChunk(m_chunkList, reqSize);

        m_chunkList = pTmp;

    }

};

 

 

class Foo3

{

public:

    Foo3(int a= 0, int b = 0):m_x(a), m_y(b){}

    void* operator new (size_t size)

    {

        if (m_pPool)

            return m_pPool->alloc(size);

    }

 

    void operator delete(void* doomed)

    {

        if (m_pPool)

            m_pPool->free(doomed);

    }

 

    static void NewMemoryPool()

    {

        m_pPool = new ByteMemoryPool();

    }

 

    static void DeleteMemoryPool()

    {

        delete m_pPool;

    }

 

private:

    int m_x;

    int m_y;

 

    static ByteMemoryPool* m_pPool;

};

 

ByteMemoryPool* Foo3::m_pPool = 0;

 

 

 

int main()

{

    DWORD time1, time2, deltaTime;

    time1 = timeGetTime();

    for (int i=0; i<500; ++i)

    {

        Foo* ptrArray[1000];

        for (int j=0; j<1000; ++j)

        {

 

            ptrArray[j] = new Foo;

        }

 

        for (int j=0; j<1000; ++j)

        {

            delete ptrArray[j];

        }

    }

 

    time2 = timeGetTime();

    deltaTime = time2- time1;

    cout<<deltaTime<<endl;

 

    Foo3::NewMemoryPool();

    time1 = timeGetTime();

    for (int i=0; i<500; ++i)

    {

        Foo3* ptrArray[1000];

        for (int j=0; j<1000; ++j)

        {

                            if (j==511)

                            {

                                     time1 = time2;

                            }

            ptrArray[j] = new Foo3;

        }

 

        for (int j=0; j<1000; ++j)

        {

            delete ptrArray[j];

        }

    }

 

    time2 = timeGetTime();

    Foo3::DeleteMemoryPool();

    deltaTime = time2- time1;

    cout<<deltaTime<<endl;

 

    return 0;

}

这里先做一下简单的说明:这个内存管理仍然是类似于固定的块,但是其分配简略进行了进一步的处理。他不再按照原来的方法一次分配一次使用。同样,其释放也不再是可重用前面分配的内存,而是一次释放回系统的堆内存中去。

释放比较简单,不做细说,说分配,分配分成了两步:

第一步,类似于固定大小内存,分配一块一块的固定大小的内存块,但是这个内存块是有一些讲究的,要与实际情况有一些具体的关系,是一个与具体环境有密切联系的相关的实验参数或者说经验参数,这个参数一般来说是不可复制的。当然,其分配内存时,是先分配一块,而固定大小的是分配N块。

第二步,在这一块中,先判断指定的大小是否可以在这块内存中分配(利用分配的大小减去使用的长度即步长),如果不可以,跳转到新的内存分配块中,并和上一块形成链表;否则,取实际需要的大小,并保存内存前进的步长,再需要时,从步长处开始,再分配指定大小的块,然后重复保存步长,重复。

再解释一下:这个内存分配管理器,只有第一块内存可以使用,因为链表后面的内存块已经被使用了。而且是不可回收的(指的是重复使用的)。

但是这个内存管理器是一个示例,应用起来还是有问题的,因为在实际的网络中,物理内存会很快耗尽,所以这里还是有相当大的改进的空间的。

一个是可以和固定大小内存管理一样,弄一个可回收的空闲链表,然后每次用完回收到空闲链表,每次新分配时,如果当前块不够用,可以去老的空闲链表里查找一下,如果找到,就用,找不到,再分配,当然,这样一定会增加时间成本。

另外还可以使用控制策略,比如过一段时间就清空一下这个链表,然后再重新分配,可这就又涉及到效率和清空的策略。

总之都得要考虑具体的应用场景和环境,不能僵化教条的使用书本上的知识。这就是太祖强烈反对的本本主义、教条主义。这些东东都害死人啊,用在程序上,当然就是害死程序了。

  评论这张
 
阅读(255)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017