C++ 内存池 -- C++ Memory Pool

本文介绍了C++内存池的概念和工作原理,详细阐述了如何通过预先分配内存、分割内存块以及管理内存块来避免堆碎片和提高内存分配速度。通过示例代码展示了内存池的创建和使用,强调了其在减少内存碎片和提升性能方面的优势。同时,文章提供了内存转储和线程安全性等方面的考虑,为读者提供了深入理解内存池的基础。
摘要由CSDN通过智能技术生成

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

这是我翻译的文章,来自 Code Project,

原文作者: DanDanger2000.

原文链接: http://www.codeproject.com/cpp/MemoryPool.asp

C++ 内存池

l  下载示例工程 – 105Kb

l  下载源代码 – 17.3Kb

 

目录
l 引言
l 它怎样工作
l 示例
l 使用这些代码
l 好处
l 关于代码
l ToDo
l 历史
 
引言
C/C++的内存分配(通过malloc或new)可能需要花费很多时。
更糟糕的是,随着时间的流逝,内存(memory)将形成碎片,所以一个应用程序的运行会越来越慢当它运行了很长时间和/或执行了很多的内存分配(释放)操作的时候。特别是,你经常申请很小的一块内存,堆(heap)会变成碎片的。
解决方案:你自己的内存池
一个(可能的)解决方法是内存池(Memory Pool)。
在启动的时候,一个”内存池”(Memory Pool)分配一块很大的内存,并将会将这个大块(block)分成较小的块(smaller chunks)。每次你从内存池申请内存空间时,它会从先前已经分配的块(chunks)中得到,而不是从操作系统。最大的优势在于:
l 非常少(几没有) 堆碎片
l 比通常的内存申请/释放(比如通过malloc, new等)的方式快
另外,你可以得到以下好处:
l 检查任何一个指针是否在内存池里
l 写一个”堆转储(Heap-Dump)”到你的硬盘(对事后的调试非常有用)
l 某种”内存泄漏检测(memory-leak detection)”:当你没有释放所有以前分配的内存时,内存池(Memory Pool)会抛出一个断言(assertion).
它怎样工作
让我们看一看内存池(Memory Pool)的UML模式图:

这个模式图只显示了类CMemoryPool的一小部分,参看由Doxygen生成的文档以得到详细的类描述。
 
一个关于内存块(MemoryChunks)的单词
你应该从模式图中看到,内存池(Memory Pool)管理了一个指向结构体SMemoryChunk (m_ptrFirstChunk, m_ptrLastChunk, and m_ptrCursorChunk)的指针。这些块(chunks)建立一个内存块(memory chunks)的链表。各自指向链表中的下一个块(chunk)。当从操作系统分配到一块内存时,它将完全的被SMemoryChunks管理。让我们近一点看看一个块(chunk)。


typedef struct SMemoryChunk
...{
  TByte *Data ;             // The actual Data
  std::size_t DataSize ;    // Size of the "Data"-Block
  std::size_t UsedSize ;    // actual used Size
  bool IsAllocationChunk ;  // true, when this MemoryChunks
                            // Points to a "Data"-Block
                            // which can be deallocated via "free()"
  SMemoryChunk *Next ;      // Pointer to the Next MemoryChunk
                            // in the List (may be NULL)

} SmemoryChunk;

每个块(chunk)持有一个指针,指针指向:
l 一小块内存(Data),
l 从块(chunk)开始的可用内存的总大小(DataSize),
l 实际使用的大小(UsedSize),
l 以及一个指向链表中下一个块(chunk)的指针。
第一步:预申请内存(pre-allocating the memory)
当你调用CmemoryPool的构造函数,内存池(Memory Pool)将从操作系统申请它的第一块(大的)内存块(memory-chunk)
/**//*Constructor
******************/
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 ;

  // Allocate the Initial amount of Memory from the Operating-System...
  AllocateMemory(sInitialMemoryPoolSize) ;
}

类的所有成员通用的初始化在此完成,AllocateMemory最终完成了从操作系统申请内存。
/**//******************
AllocateMemory
******************/
bool CMemoryPool::AllocateMemory(const std::size_t &sMemorySize)
...{
  std::size_t sBestMemBlockSize = CalculateBestMemoryBlockSize(sMemorySize) ;
  // allocate from Operating System
  TByte *ptrNewMemBlock = (TByte *) malloc (sBestMemBlockSize) ;
  ...

那么,是如何管理数据的呢?
第二步:已分配内存的分割(segmentation of allocated memory)
正如前面提到的,内存池(Memory Pool)使用SMemoryChunks管理所有数据。从OS申请完内存之后,我们的块(chunks)和实际的内存块(block)之间就不存在联系:

Memory Pool after initial allocation

 

Memory Pool after initial allocation
我们需要分配一个结构体SmemoryChunk的数组来管理内存块:
  // (AllocateMemory()continued) :
  ...
  unsigned int uiNeededChunks = CalculateNeededChunks(sMemorySize) ;
  // allocate Chunk-Array to Manage the Memory
  SMemoryChunk *ptrNewChunks =
    (SMemoryChunk *) malloc ((uiNeededChunks * sizeof(SMemoryChunk))) ;
  assert(((ptrNewMemBlock) && (ptrNewChunks))
                           && "Error : System ran out of Memory") ;
  ...

CalculateNeededChunks()负责计算为管理已经得到的内存需要的块(chunks)的数量。分配完块(chunks)之后(通过malloc),ptrNewChunks将指向一个SmemoryChunks的数组。注意,数组里的块(chunks)现在持有的是垃圾数据,因为我们还没有给chunk-members赋有用的数据。内存池的堆(Memory Pool-"Heap"):

after SMemoryChunk allocation

Memory Pool after SMemoryChunk allocation
还是那句话,数据块(data block)和chunks之间没有联系。但是,AllocateMemory()会照顾它。LinkChunksToData()最后将把数据块(data block)和chunks联系起来,并将为每个chunk-member赋一个可用的值。
// (AllocateMemory()continued) :
  ...
  // Associate the allocated Memory-Block with the Linked-List of MemoryChunks
  return LinkChunksToData(ptrNewChunks, uiNeededChunks, ptrNewMemBlock) ;

让我们看看LinkChunksToData():
/**//******************
LinkChunksToData
******************/
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 ;
    }
   
    uiMemOffSet = (i * ((unsigned int) m_sMemoryChunkSize)) ;
    m_ptrLastChunk->Data = &(ptrNewMemBlock[uiMemOffSet]) ;

    // The first Chunk assigned to the new Memory-Block will be
    // a "AllocationChunk". This means, this Chunks stores the
    // "original" Pointer to the MemBlock and is responsible for
    // "free()"ing the Memory later....
    if(!bAllocationChunkAssigned)
    ...{
      m_ptrLastChunk->IsAllocationChunk = true ;
      bAllocationChunkAssigned = true ;
    }
  }
  return RecalcChunkMemorySize(m_ptrFirstChunk, m_uiMemoryChunkCount) ;
}

让我们一步步地仔细看看这个重要的函数:第一行检查链表里是否已经有可用的块(chunks):
  ...
  if(!m_ptrFirstChunk)
  ...

我们第一次给类的成员赋值:
  ...
  m_ptrFirstChunk = SetChunkDefaults(&(ptrNewChunks[0])) ;
  m_ptrLastChunk = m_ptrFirstChunk ;
  m_ptrCursorChunk = m_ptrFirstChunk ;
  ...

m_ptrFirstChunk现在指向块数组(chunks-array)的第一个块,每一个块严格的管理来自内存(memory block)的m_sMemoryChunkSize个字节。一个”偏移量”(offset)——这个值是可以计算的所以每个(chunk)能够指向内存块(memory block)的特定部分。
    uiMemOffSet = (i * ((unsigned int) m_sMemoryChunkSize)) ;
  m_ptrLastChunk->Data = &(ptrNewMemBlock[uiMemOffSet]) ;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值