注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

勇敢的劳尤条

 
 
 

日志

 
 

内存池的原理以及实现  

2013-11-07 16:27:08|  分类: 网络编程知识积累 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

1.1 为什么要使用内存池

在编程实践中,不可避免地要大量用到堆上的内存。例如在程序中维护一个链表的数据结构时,每次新增或者删除一个链表的节点,都需要从内存堆上分配或者释放一定的内存;在维护一个动态数组时,如果动态数组的大小不能满足程序需要时,也要在内存堆上分配新的内存空间。利用默认的内存管理函数new/delete或malloc/free在堆上分配和释放内存会有一些额外的开销。
系统在接收到分配一定大小内存的请求时,首先查找内部维护的内存空闲块表,并且需要根据一定的算法(例如分配最先找到的不小于申请大小的内存块给请求者,或者分配最适于申请大小的内存块,或者分配最大空闲的内存块等)找到合适大小的空闲内存块。如果该空闲内存块过大,还需要切割成已分配的部分和较小的空闲块。然后系统更新内存空闲块表,完成一次内存分配。类似地,在释放内存时,系统把释放的内存块重新加入到空闲内存块表中。如果有可能的话,可以把相邻的空闲块合并成较大的空闲块。
默认的内存管理函数还考虑到多线程的应用,需要在每次分配和释放内存时加锁,同样增加了开销。可见,如果应用程序频繁地在堆上分配和释放内存,则会导致性能的损失。并且会使系统中出现大量的内存碎片,降低内存的利用率。默认的分配和释放内存算法自然也考虑了性能,然而这些内存管理算法的通用版本为了应付更复杂、更广泛的情况,需要做更多的额外工作。而对于某一个具体的应用程序来说,适合自身特定的内存分配释放模式的自定义内存池则可以获得更好的性能。

1.2 内存池的分类

应用程序自定义的内存池根据不同的适用场景又有不同的类型。
从线程安全的角度来分,内存池可以分为单线程内存池和多线程内存池。单线程内存池整个生命周期只被一个线程使用,因而不需要考虑互斥访问的问题;多线程内存池有可能被多个线程共享,因此则需要在每次分配和释放内存时加锁。相对而言,单线程内存池性能更高,而多线程内存池适用范围更广。
从内存池可分配内存单元大小来分,可以分为固定内存池和可变内存池。所谓固定内存池是指应用程序每次从内存池中分配出来的内存单元大小事先已经确定,是固定不变的;而可变内存池则每次分配的内存单元大小可以按需变化,应用范围更广,而性能比固定内存池要低。

1.3 内存池的优点

内存池的操作非常迅速,它在性能优化方面的优点主要如下。
(1) 针对特殊情况,例如需要频繁分配释放固定大小的内存对象时,不需要复杂的分配算法。也不需要维护内存空闲表的额外开销,从而获得较高的性能。
(2) 由于开辟一定数量的连续内存空间作为内存池块,因而一定程度上提高了程序局部性,提升了程序性能。
(3) 比较容易控制页边界对齐和内存字节对齐,没有内存碎片的问题。

1.4 内存池,完全可以从字面上理解为从池子里申请内存,释放的时候还给池子。

最简单的内存池应该是fix_pool吧,即每次分配出来的内存块大小是固定的。这种池子的管理结构是一个链表,链表的每一个节点为固定大小的内存块。分配的时候,直接返回链表的第一个节点,节点不足时,从系统申请大块内存分成多个节点加入链表;释放的时候更简单,将释放的内存加入链表头。假设fix_pool的fix size = 128,那么内存池可以为128byte以下的任意大小的请求进行分配,但是这样做相当浪费呢,于是unfix_pool就在此基础上出现了。
由多个分配大小不同的fix_pool所组成的内存池就叫做多级链表分配池。

1.5 内存池实现的主要原理

其原理是先申请一大块内存,然后分成若干个大小相等的小块,用链表的方式将这些小块链在一起,当开发人员需要使用内存时(分配),从链表头取下一块返回给开发人员使用;当开发人员使用完毕后(释放),再将这块内存重新挂回链表尾。这样操作的好处有如下三点:1、提高分配和释放的效率;2、避免开发人员忘记释放内存造成内存泄露;3、减少内存占用

1.6 类STL的内存池实现方式

SGI STL的内存池分为一级配置器和二级配置器。
一级配置器主要处理分配空间大小大于128Byte的需求,其内部实现就是直接使用malloc  realloc 和free.
二级配置器则使用使用free_list的数组链表的方式来管理内存,SGI的Allocate最小的分辨单位为8Byte,其free_list数组存着8*n(n=1...16)大小内存的首地址,大小同样的内存块使用链表的形式相连
  free_list[0] --------> 8 byte
  free_list[1] --------> 16 byte
  free_list[2] --------> 24 byte
  free_list[3] --------> 32 byte
  ... ...
  free_list[15] -------> 128 byte
 因为其对内存的管理的最小分辨度为8Byte,所以当我们申请的内存空间不是8的倍数的时候,内存池会将其调整为8的倍数大小,这叫内存对齐。当然这也免不了带来内存浪费,例如我们只需要一个10Byte的大小,内存池经过内存对齐后,会给我们一个16Byte的大小,而剩余的6Byte,在这次使用中根本没有用到。

1.7 下面的图很好的解释了1.5和1.6的内容

先申请一大块内存,然后分成若干个大小相等的小块,用链表的方式将这些小块链在一起。
维护一个free_list数组,每一个数组元素保存着一个相应大小的内存链表。

内存分配器

1.8 MemBlock是一个内存块,并串成大小固定的链。

MemBlockHeader将各个内存块又链成一个多级链表分配池,很好的诠释了1.4的讲解


2.1 为什么我要用内存池

找了这么多资料,目的也是为了写一个适合目前服务器的内存池。
我不求内存池框架多么通用,但是提升性能是绝对需要的。
因为我服务器中使用了线程池和连接池框架,为了实现这两者动态增减,写一个内存池能够更好的辅助。另外,在服务器很多地方也用到了堆空间申请,实现一个内存池是刚需。

2.2 STL内存池的学习



参考:

  评论这张
 
阅读(376)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017