图文结合 带你用纯C实现一个内存池

为什么要用内存池为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者继承的屎山中,非常容易出现内存泄漏导致mmo的问题 。
为了解决这两个问题,内存池就应运而生了 。内存池预先分配一大块内存来做一个内存池,业务中的内存分配和释放都由这个内存池来管理,内存池内的内存不足时其内部会自己申请 。所以内存碎片的问题就交由内存池的算法来优化,而内存泄漏的问题只需要遵守内存池提供的api,就非常容易避免内存泄漏了 。
即使出现了内存泄漏,排查的思路也很清晰 。1.检查是不是内存池的问题;2.如果不是内存池的问题,就检查是不是第三方库的内存泄漏 。
内存池的使用场景

  1. 全局内存池
  2. 一个连接一个内存池(本文实现这个场景的内存池)
设计一个内存池总体介绍由于本文是一个连接一个内存池,所以后续介绍和代码都是以4k为分界线,大于4k的我们认为是大块内存;小于4k的我们认为是小块内存 。并且注意这里的4k,并不是严格遵照4096,而是在描述上,用4k比较好描述 。
在真正使用内存之前,内存池提前分配一定数量且大小相等的内存块以作备用,当真正被用户调用api分配内存的时候,直接从内存块中获取内存(指小块内存),当内存块不够用了,再有内存池取申请新的内存块 。而如果是需要大块内存,则内存池直接申请大块内存再返回给用户 。
内存池:就是将这些提前申请的内存块组织管理起来的数据结构,内存池实现原理主要分为分配,回收,扩容三部分 。
内存池原理之小块内存:分配=> 内存池预申请一块4k的内存块,这里称为block,即block=4k内存块 。当用户向内存池申请内存size小于4k时,内存池从block的空间中划分出去size空间,当再有新申请时,再划分出去 。扩容=> 直到block中的剩余空间不足以分配size大小,那么此时内存池会再次申请一块block,再从新的block中划分size空间给用户 。回收=> 每一次申请小内存,都会在对应的block中引用计数加1,每一次释放小内存时,都会在block中引用计数减1,只有当引用计数为零的时候,才会回收block使他重新成为空闲空间,以便重复利用空间 。这样,内存池避免频繁向内核申请/释放内存,从而提高系统性能 。
内存池原理之大块内存:分配=> 因为大块内存是大于4k的,所以内存池不预先申请内存,也就是用户申请的时候,内存池再申请内存,然后返回给用户 。扩容=> 大块内存不存在扩容 。回收=> 对于大块内存来说,回收就直接free掉即可 。
上面理论讲完了,下面来介绍如何管理小块内存和大块内存 。
小块内存的分配与管理在创建内存池的时候,会预先申请一块4k的内存,并且在起始处将pool的结构体和node的结构体放进去,从last开始一直到end都是空闲内存,<last , end >中间的区域就用来存储小块内存 。每一次mp_malloc,就将last指针后移,直到 e n d − l a s t < s i z e end - last < size end−last<size 时,进行扩容,将新block的last后移即可 。
初始状态
图文结合 带你用纯C实现一个内存池

文章插图
 
分配内存
图文结合 带你用纯C实现一个内存池

文章插图
 
扩容
图文结合 带你用纯C实现一个内存池

文章插图
 
相关视频推荐
这是见过讲解nginx内存池,线程池最详细的视频
内存泄漏的3个解决方案与原理实现,知道一个可以轻松应对开发
学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
需要C/C++ linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享


推荐阅读