1.申请大块内存
1.1 32页<size<=128页
由于ThreadCache管理不了超过128K(即32页)的空间,所以在申请空间函数调用的时候,进行特殊处理,直接向PageCache要即可。
static void* ConcurrentAlloc(size_t size)
{
if (size > MAX_BYTES)//大于32页 ThreadCache管理不了 直接向PageCache要
{
//算对齐数
size_t alignSize = SizeClass::RoundUp(size);
//算一下需要多少页
size_t kpage = alignSize >> PAGE_SHIFT;
PageCache::GetInstance()->_pageMtx.lock();
Span* span = PageCache::GetInstance()->NewSpan(kpage);
PageCache::GetInstance()->_pageMtx.unlock();
//计算该内存的起始地址
void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
return ptr;
}
else
{
//通过ThreadCache管理
}
}
1.2 size>128页
在NewSpan()中进行特判处理,直接向堆去申请。
Span* PageCache::NewSpan(size_t k)
{
assert(k > 0);
if (k > NPAGES - 1)
{
void* ptr = SystemAlloc(k);
//Span* span = new Span;
Span* span = _spanPool.New();
span->_n = k;
span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
_idSpanMap[span->_pageId] = span;
return span;
}
//下面是正常逻辑 之前写过了
//...
}
2.脱离new操作符
高并发内存池实现的目的初衷就是为了避免每次申请空间都去调用malloc,但是可以发现我们在实现的过程中依然有使用new操作符,比如在一个大页的span分裂成两个span时,需要我们new出来一个Span对象的指针,以便操作。那要如何避免这些地方使用new操作符呢?
定长内存池,在本项目的开始就实现了一个定长内存池,他是针对于一个模板类型的内存池,而我们整个项目中,只有Span类的对象需要我们调用new,和TLS时线程获取自己的专属的ThreadCache对象所以可以满足我们的需求。
- 所有的new操作改成定长内存池的形式
- delete也要改成定长内存池的形式
- 还有在TLS中,每个线程无锁的获取自己的专属的ThreadCache对象中,我们也使用了new
3.ConcurrentFree()的优化
原本的函数定义如下
void ConcurrentFree(void* ptr, size_t size)
当我们释放一块内存的时候,不仅需要传入指针,还需要传入这块地址的大小。有时申请和释放会相隔比较远,不好确切的得到当时申请的字节数,所以需要进行优化,将这个封装好的释放调用减少一个参数,即只需要传入地址即可。
3.1 优化思路
由地址,我们可以知道页号,有了页号就可以通过_idSpanMap里面找到对应的Span,所以我们可以尝试在Span类里面再新增一个变量_objSize,用来存放这个Span会被切成的字节大小。
struct Span
{
PAGE_ID _pageId=0;// 大块内存起始页的页号
size_t _n=0;//数量
Span* _next=nullptr;
Span* _prev=nullptr;
size_t _objSize = 0;//该span被切成小内存块的大小
size_t _useCount=0;//被使用的个数
void* _freeList=nullptr;//切好的小块内存的自由链表
bool _isUse = false;
};
3.2 _objSize的初始化场景
那_objSize在什么时间初始化呢?依然从程序刚开始时进行推演,当第一次内存申请时
一、申请的内存空间小于等于32页时,该内存空间可以由ThreadCache管理,第一次申请时会调用NewSpan()函数,申请一个128字节的页块,然后将被申请的kSpan返回给central cache使用,所以我们只需要在NewSpan()函数返回的时候设置即可。
二、申请的内存空间大于128页,在申请大块空间的时候进行处理。
3.3 函数实现
static void ConcurrentFree(void* ptr)
{
Span* span = PageCache::GetInstance()->MapObjectToSpan(ptr);
size_t size = span->_objSize;
if (size > MAX_BYTES)
{
PageCache::GetInstance()->_pageMtx.lock();
PageCache::GetInstance()->ReleaseSpanToPageCache(span);
PageCache::GetInstance()->_pageMtx.unlock();
}
else
{
assert(pTLSThreadCache);
pTLSThreadCache->Deallocate(ptr, size);
}
}