学习C++也有不少时日了,今天我们来学习一个项目,该项目是借鉴谷歌的tc-malloc,让我们一起来认识一下这个设计极其优秀的项目吧
需求分析
我们该项目利用了设计模式中的池化技术,在传统的内存池上进行了优化,该内存池可以有效地提高内存申请效率以及解决内存碎片化的问题
普通内存池的优缺点
我们先来回顾一下内存池的主要思路,就是预先开辟一大块内存,当我们程序需要使用内存时,直接从该大块内存中拿取一块,可以提高申请释放效率,而不需要再去new/malloc从堆中申请内存
优点:提高效率,解决部分内存碎片问题
缺点:无法处理高并发时申请内存存在的锁竞争问题,该问题会使效率降低
我们的内存池解决的问题就是上面这些问题
主要设计思路
高并发内存池整体框架由以下三部分组成,各部分的功能如下:
那么我们就从0开始对这个项目进行实现吧
thread cache线程缓存
首先,我们思考一下,我们的这个内存池最主要的目的是什么?就是可以同时分配内存给不同的线程,基于这一点,参考我们内存池的设计,我们设计了一个成员是自由双向链表的哈希桶作为thread_cache的主要结构
private:FreeList _freeLists[NFREELISTS];//自由链表大小为NFREELISTS
那么我们就需要对双向链表进行基础实现
FreeList类
我们既然是挂在哈希桶上,要用这个链表来对内存块进行存储,所以我们结点需要1.头结点指针。2.哈希桶下结点个数。
private:void* _head = nullptr;size_t _size = 0;
我们想到,当我们一个线程需要内存时,我们将对应大小的内存返回给线程,所以我们诞生了
// 头删void* Pop()//将一份内存返回给线程{void* obj = _head;_head = NextObj(_head);_size -= 1;return obj;}
我们在这里需要对NextObj(_head)进行说明,这个函数其实是代替链表的next而生的,下面是他的具体实现
inline void*& NextObj(void* obj)//obj的下一个结点{return *((void**)obj);//我们将obj的前4/8个字节用以存储下一个节点的地址,返回obj的前4/8个字节//相当于返回了下一个结点,这里我们采用对void**解引用,就是因为使用void**才真正提取出了一块内存对//象随着32位/64位机器的不同取出前4/8个字节(解void**就是看void*的大小,而void*的大小在32位下就是4,64位就是8),注:void*一般而言是不能解引用的(Linux下可以),但是void**可以}
有了将内存传给线程,那么就会有线程将用完的内存还给内存池,随意我们的链表还有插入操作
// 头插void Push(void* obj){NextObj(obj) = _head;_head = obj;_size += 1;}
以及一些基本的函数
bool Empty(){return _head == nullptr;}size_t MaxSize(){return _max_size;}void SetMaxSize(size_t n){_max_size = n;}size_t Size(){return _size;}