内存池及实现
1.池化技术
池是一种设计模式,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,由程序自己管理,这样可以提高资源的使用效率,也可以保证本程序占有的资源数量。
如下图:
通俗的说,就是把资源放在一个池里,方便调用。经常使用的池技术包括内存池、线程池和连接池等,其中尤以内存池和线程池使用最多。
2.内存池
内存池(Memory Pool) 是一种动态内存分配与管理技术。
内存碎片问题
当对如下资源做申请、释放时候,按理是有释放掉的内存(10),但此时内存不一定连续。因此再申请大一点的内存时(如9个字节),此时申请是失败的。那么大小分别为4、6的两个内存块就是内存碎片。
通常情况下,程序员习惯直接使用 new、delete、malloc、free 等API申请分配和释放内存,当程序长时间运行时,频繁使用时会造成大量的内存碎片从而降低程序和操作系统的性能。
申请效率问题
通俗说法,假设爸妈需,给你提供生活费。
第二种方式:每次你需要钱,就向爸妈申请一次:妈我要吃早饭,给我5块;妈我要吃雪糕,给我5块等等。
第一种方式:把池想象成每个月的限额,按一个月5千算,爸妈一次给够你5000,你自行分配。当你花了1000时,池里还剩下4000,今天你勤工俭学赚了500块,你也可以放进去,这样池里就变成4500,如果5000花完了,也可以再向他们申请。
由于内存碎片问题和申请效率的问题,因此必须使用内存池
3.内存池实现
即实现一个 FreeList,每个 FreeList 用于分配固定大小的内存块,比如用于分配 32字节对象的固定内存分配器之类的。每个固定内存分配器里面有两个链表,OpenList 用于存储未分配的空闲对象,CloseList用于存储已分配的内存对象,那么所谓的分配就是从 OpenList 中取出一个对象放到 CloseList 里并且返回给用户,释放又是从 CloseList 移回到 OpenList。分配时如果不够,那么就需要增长 OpenList:申请一个大一点的内存块,切割成比如 64 个相同大小的对象添加到OpenList中。这个固定内存分配器回收的时候,统一把先前向系统申请的内存块全部还给系统。
优点:简单粗暴,200行代码就可以搞定,分配和释放的效率高,解决实际中特定场景下的问题有效。
缺点:功能单一,只能解决定长的内存需求,另外占着内存没有释放。
4.内存池的应用场景
- 需要频繁分配和释放小块内存的情况:如字符串的拼接、复制和删除等操作。内存池可以快速地从预先分配的内存块中分配内存,提高内存分配的效率。
- 需要大量内存分配和释放的情况:如数据处理、文件读写等操作。使用内存池可以避免频繁地向操作系统申请内存,减少内核态和用户态之间的切换次数,从而提高程序的性能。
- 需要长时间运行的程序:如服务器、游戏等。这些程序需要稳定、高效的内存管理,内存池可以满足这一需求。
- 需要减少内存碎片的情况:如嵌入式系统、实时操作系统等。内存池可以预先分配一定数量的内存,并将其组织为一组内存块,从而减少内存碎片的产生,提高内存的利用率。
5.内存池的优缺点
优点:
- 提高性能:通过减少向操作系统申请内存的次数,降低了内核态和用户态之间的切换次数,从而提高了程序的性能。
- 减少内存碎片:预先分配并管理内存块,可以避免频繁的内存分配和释放导致的内存碎片问题。
- 高效管理:内存池中的内存块可以被快速分配和回收,提高了内存管理的效率。
缺点:
- 内存浪费:特别是当使用固定大小内存池时,如果申请的内存大小与内存池中的内存块大小不匹配,可能会导致内存空间的浪费。
- 实现复杂:内存池的实现需要考虑多线程环境下的并发访问、内存碎片的管理等问题,实现起来相对复杂。
- 多线程竞争:在高并发环境下,多个线程同时访问内存池可能会导致性能下降和线程安全问题。
请注意,以上优缺点是基于一般情况的描述,具体的内存池实现可能会有所不同。因此,在实际应用中,需要根据具体的需求和场景来选择合适的内存池实现方式。
6、内存池和静态内存池有什么区别
内存池和静态内存池在内存管理中都扮演着重要角色,但它们之间存在明显的区别。以下是它们之间的主要差异:
定义与特点:
- 内存池:是一种内存分配方式,又被称为固定大小区块规划(fixed-size-blocks allocation)。它通常在真正使用内存之前,先申请分配一定数量的、大小相等(或相近)的内存块留作备用。内存池的目的是为了提高内存分配的效率,并尽量避免内存碎片。
- 静态内存池:是内存池的一种特定形式,实质上是一个静态数组。静态内存池内的块大小在初始化时设定,并且一旦初始化完成,内部的内存块大小将不能再做调整。静态内存池由一个控制块和若干相同大小的内存块构成,控制块位于内存池头部,用于内存块管理。
内存分配与释放:
- 内存池:在创建时先向系统申请一大块内存,然后分成大小相等的多个小内存块。当有新的内存需求时,就从内存池中分出一部分内存块。如果内存块不够,再继续申请新的内存。内存块的申请和释放以块大小为粒度。
- 静态内存池:在初始化时预设(固定)大小的内存块,并且内存块的大小在初始化后不可变更。内存块的申请和释放也是以块大小为粒度,每个内存块包含指向下一个内存块的指针。
适用场景:
- 内存池:适用于需要频繁分配和释放小块内存,或者需要大量内存分配和释放的场景。例如,字符串的拼接、复制和删除等操作,以及数据处理、文件读写等操作。
- 静态内存池:更适用于需要长时间运行的程序,或者需要固定长度内存块的场景。当用户需要使用固定长度的内存时,可以通过静态内存分配的方式获取内存,一旦使用完毕,通过静态内存释放函数归还所占用内存,使之可以重复使用。
优缺点:
- 内存池:优点在于提高了内存分配的效率,减少了内存碎片。缺点在于如果申请的内存大小与内存池中的内存块大小不匹配,可能会导致内存空间的浪费。
- 静态内存池:优点在于分配和释放效率高,静态内存池中无碎片。缺点在于只能申请到初始化预设大小的内存块,不能按需申请,且一旦初始化完成,内存块大小不可调整。
综上所述,内存池和静态内存池在定义、内存分配与释放、适用场景以及优缺点等方面存在明显的区别。选择使用哪种内存管理方式,需要根据具体的应用场景和需求来决定。