这里写目录标题
思考
- 什么是内存池?内存池是做什么的?核心是什么?(心想这不是废话吗!肯定时管理内存的,具体呢)。
- 内存池这个所谓的“池子”怎么建造比较好?
- 内存池的使用场景?
- 有什么好的“轮子”使用?要不要自己“造轮子”?什么时候真的适合自己“造轮子”?如果造轮子,怎么造一个好用“轮子”?
充电站
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习
什么是内存池?
形象解释 :它的名字起的还是很贴切的,就像一个人叫“胖虎”,那他肯定是比较胖的,哈哈。起一个不太恰当的例子,一个所谓的“渣男”、“渣女”有一个“池塘”,更有甚者是“海洋”。有了这样的池塘以后就很方便,需要了就直接取用。比如“渣女”今天想吃水果了,心情不好需要安慰。 在“池塘”里捞一个“老实人”就很方便的达到目的。如果没有这样的池塘,自己又不想动,取寻找培养一个“老实人”是很花费时间的,达到目的后就冷落了,如果彻底断绝联系吧,万一以后还有有用的地方对吧?所以干脆放在自己的“鱼塘”。(这里只是举个例子,希望不要被喷啊)。 学术点解释 :池
是在计算技术中经常使用的一种设计模式,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,有程序自管理,这样可以提高资源的利用率,也可以保证本程序占有的资源数量。内存池(Memory Pool)
是一种 动态内存分配 与管理技术,内存池则是在真正使用内存之前,先申请分配一大块内存(内存池)留作备用。当程序员申请内存时,从池中取出一块动态分配,当程序员释放时,将释放的内存放回到池内,再次申请,就可以从池里取出来使用,并尽量与周边的空闲内存块合并。
内存池作用
内存池对一个空白的内存进行维护的过程,主要是对
堆
进行管理的。其核心
是避免频繁的内存分配与释放。Linux下进程开始运行的时候,内存就 被划分出来了,这里面主要包括
内核空间
和用户空间
。用户空间主要能管理的就是mmap与堆,mmap有些进程没有,所以主要的还是对堆的管理。
客户端与服务器连接的中,当客户端
send()
数据给服务器,服务器在recv()
之前需要有一块内存进行数据的存储。肯定会想到定义一个char buffer[]
,但时定义的buffer在栈区,随着函数的返回会被释放掉。这就需要在recv()
之前malloc()
出一个内存空间,给recv()
去使用。这个过程可能还伴随着数据库的连接,recv()完以后进行业务处理。malloc()出来的内存存储的数据生命周期在整个连接过程中。在多个客户端进进行服务器连接的过程,必然会带来内存
频繁的连接与释放
。这样在申请和释放的过程造成堆
利用率很低的一个主要原因就是内存碎片化。如果有未使用的存储器,但是这块存储器不能用来满足分配的请求,这时候就会产生内存碎片化
问题。
综上所述:解决内存频繁分内,造成内存碎片化的方法就是
内存池
,再提一点,再工作当中,尽量是要用,不要自己造,但原理一定要明白
。
内存池的演变
版本一
最早的内存池雏形,malloc()申请一次内存,就用链表组织起来。将用到的内存加入uselist中,并且重载free()函数,设置flag。当释放内存是,用flag标记该内存未使用。当下次再需要内存是就在链表中找,再进行利用。
伪代码
// 伪代码 //节点结构体 struct memnode { void *addr; int size; int flag; //1 use, 0 free struct memonde *next; } //定义内存池 struct memnode *mempool; //重载malloc() void *nmalloc() { void *addr = malloc(); struct memnode *node; ADD(mempool, node); //加入链表 }
版本二
当然初始的内存池也存在较多的问题:会出现
内存块越来越小
的问题。提出按照
不同的大小l来配固定大小的块
。例如,假定最小的内存块为16Byte,可以进行32Byte、64Byte、128Byte、256Byte、512Byte、1024Byte。假设大于1024Byte为大内存块,交给系统进行分配和释放。其实先内存分配的过程中往往最难处理的就是这些小块内存块。这里引入小块
和大块
的概念。
伪代码
// 伪代码 //节点结构体 struct memnode { void *addr; int size; int flag; //1 use, 0 free struct memonde *next; } //定义内存池 struct memnode *mempool; //重载malloc() void *nmalloc(int size) { void * addr = search(usetable, size); //循环做 if (addr = NULL) { void *addr = malloc(size); struct memnode *node; ADD(mempool, node); //加入链表 } } //重载free() void nfree(void *addr) { struct memnode *node = search_from(addr); node->flag = 0; }
版本二的方式仍然有一些问题,这里提出来。主要是查找速度慢
,内存块不是连在一起的(无法合并成一个大块)会有间隙
,后续我们会继续讨论。
总结
本文主要介绍了什么是内存池,内存池简单来说是一种动态内存分配与管理技术。其核心避免频繁的内存分配与释放,减少内存碎片。并举例在CS模型中的体现。并且介绍了内存池一个演变的过程,从最早的内存池雏形,到版本二较为实用的方式。
后续
后续还需继续对内存池进行分析,后续更精彩。