高并发内存池(ConCurrentMemoryPool)

一、项目概述

1.简介

基于Google开源tcmalloc项目,该内存池主要应用于多线程频繁申请和释放大量小块内存。它的优点:性能卓越、大幅减少了外内存碎片。

2.工具

Google的tcmalloc开源代码,使用vs2019进行研究学习。

3.目标

  1. 建立一个可行的私人高并发内存池,需要解决以下三个问题:
    1. 效率问题
    2. 内存碎片问题
    3. 多线程并发场景下的内存释放和申请的锁竞争问题。
  2. 深入理解内存管理

4.扩展

  1. 完全脱离malloc和free,且不要使用thread和mutex
  2. 处理不支持本地线程存储(TLS)的跨平台问题
  3. 将静态TLS更新为动态TLS
  4. CentralCache和page cache目前是一个全局对象,更新为单例模式
  5. 接入STL库,使之正常使用

二、项目背景

1.直接使用new/delete、malloc/free存在的问题

        new/delete用于c++中动态内存管理而malloc/free在c++和c中都可以使用,本质上new/delete底层封装了malloc/free。无论是上面的那种内存管理方式,都存在以下两个问题:

  • 效率问题:频繁的在堆上申请和释放内存必然需要大量时间,降低了程序的运行效率。对于一个需要频繁申请和释放内存的程序来说,频繁调用new/malloc申请内存,delete/free释放内存都需要花费系统时间,频繁的调用必然会降低程序的运行效率。
  • 内存碎片:经常申请小块内存,会将物理内存“切”得很碎,导致内存碎片。申请内存的顺序并不是释放内存的顺序,因此频繁申请小块内存必然会导致内存碎片,造成“有内存但是申请不到大块内存”的现象。

2.定长内存池的优点和缺点

        针对直接使用new/delete、malloc/free存在的问题,定长内存池的设计思路是:预先开辟一块大内存,程序需要内存时直接从该大块内存中“拿”一块,提高申请和释放内存的效率,同时直接分配大块内存还减少了内存碎片问题。

  • 优点:简单粗暴,分配和释放的效率高,解决实际中特定场景下的问题有效。
  • 缺点:功能单一,只能解决定长的内存需求,另外占着内存没有释放。

对于STL中的空间配置器就是采用的这种方式,当申请小于128字节的内存就是用定长内存池,当超过时,就直接使用malloc和free

三、整体设计

1.框架

        高并发内存池整体框架由以下三部分组成,各部分的功能如下:

  1. 线程缓存(ThreadCache):每个线程独有线程缓存,主要解决多线程下高并发运行场景线程之间的锁竞争问题。线程缓存模块可以为线程提供小于64k内存的分配,并且多个线程并发运行不需要加锁。线程从这里申请内存不需要加锁,每个线程独享一个ThreadCache,这也就是这个并发内存池高效的地方(这就是tcmalloc名字的本质来源,在这里具体的实现采用的是TLS(thread local storage 本地线程存储,可以理解为每个线程独有的全局变量,但是是本地的)))。(本质上ThreadCache里面就是由hash映射的定长的内存桶)
  2. 中心缓存(CentralCache):中心缓存是所有线程所共享,thread cache是按需从CentralCache中获取的对象。CentralCache周期性的回收thread cache中的对象,避免一个线程占用了太多的内存,而其他线程的内存吃紧。达到内存分配在多个线程中更均衡的按需调度的目的。CentralCache是存在竞争的,所以从这里取内存对象是需要加锁,(比如说一个线程Thread Cache下的8字节映射的自由链表过长,就需要还回CentralCache,但是此时另外的一个线程Thread Cache下8字节的自由链表没有了,就需要向CentralCache要,此时就需要加锁,因为这种情况很少会出现)不过一般情况下在这里取内存对象的效率非常高,所以这里竞争不会很激烈。
  3. 页缓存(PageCache):以页为单位申请内存,为中心控制缓存提供大块内存。当中心控制缓存中没有内存对象时,可以从PageCache中以页为单位按需获取大块内存,同时PageCache还会回收CentralCache的内存进行合并缓解内存碎片问题。

2.申请流程

        我们的并发内存池项目对外只暴露两个接口,对于申请的接口就是ConcurrentAlloc(),如果申请的内存大于64KB,直接走的就是PageCache所提供的的NewSpan(),但是走这个也有可能有两种情况,一种是介于16页——128页之间,一开始就会直接的申请上来一块128页的内存然后进行切分,返回你需要的那一部分。大于128页直接调用VirtualAlloc进行向系

你可能感兴趣的:(数据结构,c++,开发语言)