建议35:使用内存池技术提高内存申请效率与性能
Doug Lea曾有言曰:“自1960年以来,动态内存分配就已经成为大多计算机系统的重要部分。”
动态内存管理确实是件让人头疼的事儿,然而在实际的编程实践中,又不可避免地要大量用到堆上的内存。而这些通过malloc或new进行的内存分配却有着一些天生的缺陷:一方面,利用默认的内存管理函数在堆上分配和释放内存会有一些额外的开销,需要花费很多时间;另一方面,也是更糟糕的,随着时间的流逝,内存将形成碎片,一个应用程序的运行会越来越慢。
当程序中需要对相同大小的对象频繁申请内存时,常会采用内存池(Memory Pool)技术来提高内存申请效率。经典的内存池技术,是一种用于分配大量大小相同的小对象的技术。通过该技术可以极大地加快内存分配/释放过程。内存池技术通过批量申请内存,降低了内存申请次数,从而节省了时间。对于大批量的小对象而言,使用内存池技术整体申请内存,减少了内存碎片的产生,对性能提升的帮助也是很显著的。
内存池技术的基本原理通过这个“池”字就进行了很好的自我阐释:应用程序可以通过系统的内存分配调用预先一次性申请适当大小的内存块(Block),并会将它分成较小的块(Smaller Chunks),之后每次应用程序会从先前已经分配的块(chunks)中得到相应的内存空间,对象分配和释放的操作都可以通过这个“池”来完成。只有当“池”的剩余空间太小,不能满足应用程序需要时,应用程序才会再调用系统的内存分配函数对其大小进行动态扩展。
经典的内存池实现原理如下:
|
其中MemPool涉及两个常量:m_nMemBlockSize、m_nItemSize,还有两个指针变量m_pMemBlockHeader、m_pFreeNodeHeader。指针变量m_pMemBlockHeader是用来把所有申请的内存块(MemBlock)串成一个链表。m_pFreeNodeHeader变量则是把所有自由的内存结点(FreeNode)串成一个链表。内存块在申请之初就被划分为了多个内存结点,每个结点的大小为ItemSize(对象的大小),共计MemBlockSize/ItemSize个。然后,这些内存结点会被串成链表。每次分配的时候从链表中取一个给用户,不够时继续向系统申请大块内存。在释放内存时,只须把要释放的结点添加到自由内存链表m_pFreeNodeHeader中即可。在MemPool对象析构时,可完成对内存的最终释放。
Boost库同样对该技术提供了较好的支持:
|
【福利】填问卷送精选测试礼包+接口测试课程!为测试行业做点事!