简明分析C/C++内存分配的解决方案

时间:2023-02-06 04:00:41
 

简明分析C/C++内存分配的解决方案

http://developer.51cto.com  2008-12-01 09:09  佚名  Chinaitlab   我要评论(0)

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.h

#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;}