Chinaunix首页 | 论坛 | 博客
  • 博客访问: 829860
  • 博文数量: 125
  • 博客积分: 4066
  • 博客等级: 上校
  • 技术积分: 1401
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-03 18:58
文章分类

全部博文(125)

文章存档

2014年(1)

2013年(1)

2012年(2)

2011年(29)

2010年(92)

我的朋友

分类: LINUX

2010-06-28 17:07:28

可以参考:

 基于C语言实现的内存池

http://blog.csdn.net/ugg/archive/2007/03/27/1543290.aspx


C/C++的内存分配(通过malloc或new)可能需要花费很多时。

更糟糕的是,随着时间的流逝,内存(memory)将形成碎片,所以一个应用程序的运行会越来越 慢。当它运行了很长时间和/或执行了很多的内存分配(释放)操作的时候。特别是,你经常申请很小的一块内存,堆(heap)会变成碎片的。

解决方案:你自己的内存池一个(可能的)解决方法是内存池(Memory Pool)。

在启动的时候,一个“内存池”(Memory Pool)分配一块很大的内存,并将会将这个大块(block)分成较小的块(smaller chunks)。每次你从内存池申请内存空间时,它会从先前已经分配的块(chunks)中得到,而不是从操作系统。最大的优势在于:

1:非常少(几没有) 堆碎片

2: 比通常的内存申请/释放(比如通过malloc, new等)的方式快另外,你可以得到以下好处:1:检查任何一个指针是否在内存池里2:写一个“堆转储(Heap-Dump)”到你的硬盘(对事后的调试 非常有用)

3: 某种“内存泄漏检测(memory-leak detection)”:当你没有释放所有以前分配的内存时,内存池(Memory Pool)会抛出一个断言(assertion)。


缺点:

这种固定快大小的链表,这种内存池的缺点是灵活性不足;另外还有基于大块内存的可变长度内存分配方法,优点是对于字符串类似的应 用提供了很好的解决方法,缺点是要自己管理碎片,相当于在系统的内存管理之上再构建一个内存管理,同样有查找合适内存快的开销;

本文采用的是固定内存快的内存池,另外对于多线程还需要系统锁,也会增加开销,如何权衡需要多种方案对比才能得出结论。

SMemoryChunk.h

#ifndef __SMEMORYCHUNK_H__
#define __SMEMORYCHUNK_H__

typedef unsigned char TByte ;

struct SMemoryChunk
{
  TByte *Data; //数据

  std::size_t DataSize; //该内存块的总大小

  std::size_t UsedSize; //实际使用的大小

  bool IsAllocationChunk;
  SMemoryChunk *Next; //指向链表中下一个块的指针。

};

#endif



IMemoryBlock.h



#ifndef __IMEMORYBLOCK_H__
#define __IMEMORYBLOCK_H__

class IMemoryBlock
{
  public :
    virtual ~IMemoryBlock() {};

    virtual void *GetMemory(const std::size_t &sMemorySize) = 0;
    virtual void FreeMemory(void *ptrMemoryBlock, const std::size_t &sMemoryBlockSize) = 0;

};

#endif


CMemoryPool.h


#ifndef __CMEMORYPOOL_H__
#define __CMEMORYPOOL_H__

#include "IMemoryBlock.h"
#include "SMemoryChunk.h"

static const std::size_t DEFAULT_MEMORY_POOL_SIZE = 1000;//初始内存池的大小

static const std::size_t DEFAULT_MEMORY_CHUNK_SIZE = 128;//Chunk的大小

static const std::size_t DEFAULT_MEMORY_SIZE_TO_ALLOCATE = DEFAULT_MEMORY_CHUNK_SIZE * 2;

class CMemoryPool : public IMemoryBlock
{
public:
    CMemoryPool(const std::size_t &sInitialMemoryPoolSize = DEFAULT_MEMORY_POOL_SIZE,
                const std::size_t &sMemoryChunkSize = DEFAULT_MEMORY_CHUNK_SIZE,
                const std::size_t &sMinimalMemorySizeToAllocate = DEFAULT_MEMORY_SIZE_TO_ALLOCATE,
                bool bSetMemoryData = false
                );

    virtual ~CMemoryPool();

    //从内存池中申请内存

    virtual void* GetMemory(const std::size_t &sMemorySize);
    virtual void FreeMemory(void *ptrMemoryBlock, const std::size_t &sMemoryBlockSize);
    
private:
    //申请内存OS

    bool AllocateMemory(const std::size_t &sMemorySize);
    void FreeAllAllocatedMemory();
    
    //计算可以分多少块

    unsigned int CalculateNeededChunks(const std::size_t &sMemorySize);

    //计算内存池最合适的大小

    std::size_t CMemoryPool::CalculateBestMemoryBlockSize(const std::size_t &sRequestedMemoryBlockSize);
    
    //建立链表.每个结点Data指针指向内存池中的内存地址

    bool LinkChunksToData(SMemoryChunk* ptrNewChunks, unsigned int uiChunkCount, TByte* ptrNewMemBlock);
    
    //重新计算块(Chunk)的大小1024--896--768--640--512------------

    bool RecalcChunkMemorySize(SMemoryChunk* ptrChunk, unsigned int uiChunkCount);
    
    SMemoryChunk* SetChunkDefaults(SMemoryChunk *ptrChunk);

    //搜索链表找到一个能够持有被申请大小的内存块(Chunk).如果它返回NULL,那么在内存池中没有可用的内存

    SMemoryChunk* FindChunkSuitableToHoldMemory(const std::size_t &sMemorySize);

    std::size_t MaxValue(const std::size_t &sValueA, const std::size_t &sValueB) const;
    
    void SetMemoryChunkValues(SMemoryChunk *ptrChunk, const std::size_t &sMemBlockSize);

    SMemoryChunk* SkipChunks(SMemoryChunk *ptrStartChunk, unsigned int uiChunksToSkip);

private:

    SMemoryChunk *m_ptrFirstChunk;
    SMemoryChunk *m_ptrLastChunk;
    SMemoryChunk *m_ptrCursorChunk;

    std::size_t m_sTotalMemoryPoolSize; //内存池的总大小

    std::size_t m_sUsedMemoryPoolSize; //以使用内存的大小

    std::size_t m_sFreeMemoryPoolSize; //可用内存的大小


    std::size_t m_sMemoryChunkSize; //块(Chunk)的大小

    unsigned int m_uiMemoryChunkCount; //块(Chunk)的数量

    unsigned int m_uiObjectCount;

    bool m_bSetMemoryData ;
    std::size_t m_sMinimalMemorySizeToAllocate;

};

#endif


CMemoryPool.c


#include "stdafx.h"
#include "CMemorypool.h"

#include
#include

static const int NEW_ALLOCATED_MEMORY_CONTENT = 0xFF ;


CMemoryPool::CMemoryPool(const std::size_t &sInitialMemoryPoolSize,
                         const std::size_t &sMemoryChunkSize,
                         const std::size_t &sMinimalMemorySizeToAllocate,
                         bool bSetMemoryData)
{
    m_ptrFirstChunk = NULL;
    m_ptrLastChunk = NULL;
    m_ptrCursorChunk = NULL;

    m_sTotalMemoryPoolSize = 0;
    m_sUsedMemoryPoolSize = 0;
    m_sFreeMemoryPoolSize = 0;

    m_sMemoryChunkSize = sMemoryChunkSize;
    m_uiMemoryChunkCount = 0;
    m_uiObjectCount = 0;

    m_bSetMemoryData = !bSetMemoryData;
    m_sMinimalMemorySizeToAllocate = sMinimalMemorySizeToAllocate;

    AllocateMemory(sInitialMemoryPoolSize);
}

CMemoryPool::~CMemoryPool()
{

}

void* CMemoryPool::GetMemory(const std::size_t &sMemorySize)
{
    std::size_t sBestMemBlockSize = CalculateBestMemoryBlockSize(sMemorySize);
    SMemoryChunk* ptrChunk = NULL;
    while(!ptrChunk)
    {

        ptrChunk = FindChunkSuitableToHoldMemory(sBestMemBlockSize);

        //ptrChunk等于NULL表示内存池内存不够用

        if(!ptrChunk)
        {
            sBestMemBlockSize = MaxValue(sBestMemBlockSize, CalculateBestMemoryBlockSize(m_sMinimalMemorySizeToAllocate));
            //从OS申请更多的内存

            AllocateMemory(sBestMemBlockSize);
        }
    }
    //下面是找到可用的块(Chunk)代码

    m_sUsedMemoryPoolSize += sBestMemBlockSize;
    m_sFreeMemoryPoolSize -= sBestMemBlockSize;
    m_uiObjectCount++;
    //标记该块(Chunk)已用

    SetMemoryChunkValues(ptrChunk, sBestMemBlockSize);

    return ((void *) ptrChunk->Data);
}

void CMemoryPool::FreeMemory(void *ptrMemoryBlock, const std::size_t &sMemoryBlockSize)
{

}

bool CMemoryPool::AllocateMemory(const std::size_t &sMemorySize)
{
    //计算可以分多少块(1000 / 128 = 8)

    unsigned int uiNeededChunks = CalculateNeededChunks(sMemorySize);

    //当内存池的初始大小为1000字节,块(Chunk)大小128字节,分8块还差24字节.怎么办?

    //解决方案:多申请24字节

    std::size_t sBestMemBlockSize = CalculateBestMemoryBlockSize(sMemorySize);

    //向OS申请内存

    TByte *ptrNewMemBlock = (TByte*) malloc(sBestMemBlockSize);

    //分配一个结构体SmemoryChunk的数组来管理内存块

    SMemoryChunk *ptrNewChunks = (SMemoryChunk*) malloc((uiNeededChunks * sizeof(SMemoryChunk)));

    m_sTotalMemoryPoolSize += sBestMemBlockSize;
    m_sFreeMemoryPoolSize += sBestMemBlockSize;
    m_uiMemoryChunkCount += uiNeededChunks;

    if(m_bSetMemoryData)
    {
        memset(((void *) ptrNewMemBlock), NEW_ALLOCATED_MEMORY_CONTENT, sBestMemBlockSize);
    }

    return LinkChunksToData(ptrNewChunks, uiNeededChunks, ptrNewMemBlock);
}

unsigned int CMemoryPool::CalculateNeededChunks(const std::size_t &sMemorySize)
{
    float f = (float) (((float)sMemorySize) / ((float)m_sMemoryChunkSize));
    return ((unsigned int) ceil(f));
}

std::size_t CMemoryPool::CalculateBestMemoryBlockSize(const std::size_t &sRequestedMemoryBlockSize)
{
    unsigned int uiNeededChunks = CalculateNeededChunks(sRequestedMemoryBlockSize);
    return std::size_t((uiNeededChunks * m_sMemoryChunkSize));
}

bool CMemoryPool::LinkChunksToData(SMemoryChunk* ptrNewChunks, unsigned int uiChunkCount, TByte* ptrNewMemBlock)
{
    SMemoryChunk *ptrNewChunk = NULL;
    unsigned int uiMemOffSet = 0;
    bool bAllocationChunkAssigned = false ;
    for(unsigned int i = 0; i < uiChunkCount; i++)
    {
        //建立链表

        if(!m_ptrFirstChunk)
        {
            m_ptrFirstChunk = SetChunkDefaults(&(ptrNewChunks[0]));
            m_ptrLastChunk = m_ptrFirstChunk;
            m_ptrCursorChunk = m_ptrFirstChunk;
        }
        else
        {
            ptrNewChunk = SetChunkDefaults(&(ptrNewChunks[i]));
            m_ptrLastChunk->Next = ptrNewChunk;
            m_ptrLastChunk = ptrNewChunk;
        }
        //根据块(Chunk)的大小计算下一块的内存偏移地址

        uiMemOffSet = (i * ((unsigned int) m_sMemoryChunkSize));

        //结点指向内存偏移地址

        m_ptrLastChunk->Data = &(ptrNewMemBlock[uiMemOffSet]);


        if(!bAllocationChunkAssigned)
        {
            m_ptrLastChunk->IsAllocationChunk = true;
            bAllocationChunkAssigned = true;
        }
    }
    return RecalcChunkMemorySize(m_ptrFirstChunk, m_uiMemoryChunkCount);
}

bool CMemoryPool::RecalcChunkMemorySize(SMemoryChunk *ptrChunk, unsigned int uiChunkCount)
{
    unsigned int uiMemOffSet = 0 ;
    for(unsigned int i = 0; i < uiChunkCount; i++)
    {
        if(ptrChunk)
        {
            uiMemOffSet = (i * ((unsigned int) m_sMemoryChunkSize)) ;
            ptrChunk->DataSize = (((unsigned int) m_sTotalMemoryPoolSize) - uiMemOffSet);
            ptrChunk = ptrChunk->Next ;
        }
        else
        {
            assert(false && "Error : ptrChunk == NULL");
            return false;
        }
    }
    return true;
}

SMemoryChunk* CMemoryPool::SetChunkDefaults(SMemoryChunk* ptrChunk)
{
    if(ptrChunk)
    {
        ptrChunk->Data = NULL;
        ptrChunk->DataSize = 0;
        ptrChunk->UsedSize = 0;
        ptrChunk->IsAllocationChunk = false;
        ptrChunk->Next = NULL;
    }
    return ptrChunk;
}

SMemoryChunk *CMemoryPool::FindChunkSuitableToHoldMemory(const std::size_t &sMemorySize)
{
    unsigned int uiChunksToSkip = 0;
    bool bContinueSearch = true;
    SMemoryChunk *ptrChunk = m_ptrCursorChunk;
    for(unsigned int i = 0; i < m_uiMemoryChunkCount; i++)
    {
        if(ptrChunk)
        {
            if(ptrChunk == m_ptrLastChunk)
            {
                ptrChunk = m_ptrFirstChunk;
            }

            if(ptrChunk->DataSize >= sMemorySize)
            {
                if(ptrChunk->UsedSize == 0)
                {
                    m_ptrCursorChunk = ptrChunk;
                    return ptrChunk;
                }
            }
            uiChunksToSkip = CalculateNeededChunks(ptrChunk->UsedSize);
            if(uiChunksToSkip == 0) uiChunksToSkip = 1;
            ptrChunk = SkipChunks(ptrChunk, uiChunksToSkip);
        }
        else
        {
            bContinueSearch = false
        }
    }
    return NULL;
}

std::size_t CMemoryPool::MaxValue(const std::size_t &sValueA, const std::size_t &sValueB) const
{
    if(sValueA > sValueB)
    {
        return sValueA;
    }
    return sValueB;
}

void CMemoryPool::SetMemoryChunkValues(SMemoryChunk *ptrChunk, const std::size_t &sMemBlockSize)
{
    if((ptrChunk))
    {
        ptrChunk->UsedSize = sMemBlockSize;
    }
    else
    {
        assert(false && "Error : Invalid NULL-Pointer passed");
    }
}

SMemoryChunk *CMemoryPool::SkipChunks(SMemoryChunk *ptrStartChunk, unsigned int uiChunksToSkip)
{
    SMemoryChunk *ptrCurrentChunk = ptrStartChunk;
    for(unsigned int i = 0; i < uiChunksToSkip; i++)
    {
        if(ptrCurrentChunk)
        {
            ptrCurrentChunk = ptrCurrentChunk->Next;
        }
        else
        {
            assert(false && "Error : Chunk == NULL was not expected.");
            break ;
        }
    }
    return ptrCurrentChunk;
}


测试方法:


// 111.cpp : 定义控制台应用程序的入口点。

//


#include "stdafx.h"

#include "CMemoryPool.h"

CMemoryPool* g_pMemPool = NULL;

class testMemoryPool
{
public:
    testMemoryPool(){
    }

    void *operator new(std::size_t ObjectSize)
    {
        return g_pMemPool->GetMemory(ObjectSize) ;
    }

private:
    char a[25];
    bool b;
    long c;
};//sizeof(32);


int _tmain(int argc, _TCHAR* argv[])
{
    g_pMemPool = new CMemoryPool();

    testMemoryPool* test = new testMemoryPool();

    return 0;
}


阅读(1355) | 评论(0) | 转发(0) |
0

上一篇:传输文件命令---wget

下一篇:setlocale() 函数

给主人留下些什么吧!~~