概述
在c/c++中,内存分配(如malloc或new)会使用很多时间。
一个程序会随着长时间的运行和内存的申请释放而变得越来越慢,内存也会随着时间逐渐碎片化。特别是高频率的进行小内存申请释放,此问题变得尤其严重。
相关视频讲解:
你或许不知道高性能服务器为什么需要内存池?内存如何分配? 如何设计内存 ?
Linux后端开发网络底层原理知识学习提升,完善技术栈,内容知识点包括Linux,Nginx,ZeroMQ,MySQL,Redis,线程池,MongoDB,ZK,Linux内核,CDN,P2P,epoll,Docker,TCP/IP,协程,DPDK等等。
点击观看:c/c++Linux后台服务器开发高级架构师学习视频资料
解决方案:定制内存池
为解决上述问题,一个(可能的)的解决方案就是使用内存池。
“内存池”在初始化时,分配一个大块内存(称 原始内存块),并且将此内存分割为一些小的内存块。当你需要请求分配内存时,则从内存池中取出事先分配好的内存,而不是向OS申请。内存池最大的优势在于:
1、极少的(甚至没有)堆碎片整理
2、较之普通内存分配(如malloc,new),有着更快的速度
额外的,你还将获得如下好处:
1、检测任意的指针是否指向内存池内
2、生成"heap-dump"
3、各种 内存泄漏 检测:当你没有释放之前申请的内存,内存池将抛出断言
如何工作?
让我们看看内存池的UML模型图:

图中简要的描述了CMemoryPool class,更多的细节请查看源码中class声明。
那么,CMemoryPool如何实际工作?
关于 MemoryChunks
正如你在UML图中所看到的,内存池维护着一个SMemoryChunk链表,并管理着三个指向SMemoryChunk结构的指针(m_ptrFirstChunk, m_ptrLastChunk, and m_ptrCursorChunk)。这些指针指向SMemoryChunk链表的不同位置。让我们更深入的观察SMemoryChunk:(在内存池实现中,SMemoryChunk封装了原始内存块的各个部分 -- 译者注)
typedef struct SMemoryChunk
{
TByte *Data ; // 常规数据指针
std::size_t DataSize ; // 内存块容量
std::size_t UsedSize ; // 内存块当前使用大小
bool IsAllocationChunk ; // 为true时, 内存块已被分配,可用free之类的函数释放
SMemoryChunk *Next ; // 指向内存块链表中的下一个内存块,可能为null
第一步:预分配内存
当你调用CMemoryPool的构造函数,内存池会向OS申请原始内存块。
/******************
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 ;

本文探讨了C++中内存碎片和分配速度的问题,并提出使用内存池作为解决方案。内存池通过预先分配大块内存并分割成小块,提供更快的分配速度和减少碎片。文章详细介绍了内存池的工作原理,包括预分配内存、内存分块和向内存池请求内存的步骤,并提供了源码示例。此外,还提到了内存诊断和未来改进的方向,如减少内存开销和实现线程安全性。
最低0.47元/天 解锁文章
1322





