1. 前言
请先看完页缓存的具体实现(上)
2. 什么是内存碎片问题?
我们拿整个程序地址空间来举例:
3. 地址空间上的内存使用情况
在地址空间中,一共是4GB大小的空间.
地址从0000 0000到FFFF FFFF.
4. 页缓存合并内存的代码实现
在pagecache.h文件中:
void PageCache::ReleaseSpanToPageCache(SpanData* span)
{
if (span->_n > N_PAGES - 1)//大于128页的内存直接还给堆,不需要走pagecache
{
void* ptr = (void*)(span->_pageid << PAGE_SHIFT);
SystemFree(ptr);
//delete span;
_spanPool.Delete(span);
return;
}
//对span前后的页尝试进行合并,缓解外碎片问题
while (1)//不断往前合并,直到遇见不能合并的情况
{
PAGE_ID prevId = span->_pageid - 1;
auto prevret = _idSpanMap.find(prevId);
if (prevret == _idSpanMap.end())//前面没有页号了
break;
SpanData* prevspan = ret;
if (ret == nullptr)
break;
if (prevspan->_isUse == true)//前面的页正在使用
break;
if (prevspan->_n + span->_n > N_PAGES - 1)//当前页数加上span的页数大于128了,pagecache挂不下了
break;
//开始合并span和span的前面页
span->_pageid = prevspan->_pageid;
span->_n += prevspan->_n;
_spanList[prevspan->_n].Erase(prevspan);//将被合并的页从pagecache中拿下来
//delete prevspan;//将prevspan中的数据清除,诸如页号,页数等
_spanPool.Delete(prevspan);
}
while (1)//不断往后合并,直到遇见不能合并的情况
{
PAGE_ID nextId = span->_pageid + span->_n;
auto nextret = _idSpanMap.find(nextId);
if (nextret == _idSpanMap.end())//前面没有页号了
break;
//SpanData* nextspan = nextret->second;
auto ret = (SpanData*)_idSpanMap.get(nextId);
if (ret == nullptr)
break;
SpanData* nextspan = ret;
if (nextspan->_isUse == true)//前面的页正在使用
break;
if (nextspan->_n + span->_n > N_PAGES - 1)//当前页数加上span的页数大于128了,pagecache挂不下了
break;
//开始合并span和span的前面页
span->_n += nextspan->_n;
_spanList[nextspan->_n].Erase(nextspan);//将被合并的页从pagecache中拿下来
//delete nextspan;//将prevspan中的数据清除,诸如页号,页数等
_spanPool.Delete(nextspan);
}
//合并完后将span挂起来
_spanList[span->_n].PushFront(span);
//合并完后,要重新将这个span的首尾两页的id和这个span进行映射,方便别的span来合并我的时候使用
_idSpanMap[span->_pageid] = span;
_idSpanMap[span->_pageid + span->_n - 1] = span;
span->_isUse = false;
}
5. 总结以及对代码的拓展
页缓存结构的讲解已经结束,现在回头来看前面设计的这三层缓存结构,可谓是非常之巧妙,第一层线程缓存是无锁的,申请/释放内存非常高效,而第二层中心缓存是用的桶锁,在大多数情况下也没有竞争锁的问题,效率也非常高,所以现在能理解为什么要设计三层而不是两层,甚至是一层,一方面是为了效率的考量,另一方面是为了可以方便合并相邻的空闲页
在使用到了直接向系统返还内存的函数:
inline static void SystemFree(void* ptr)
{
#ifdef _WIN32
VirtualFree(ptr, 0, MEM_RELEASE);
#else
// sbrk unmmap等
#endif
}
同样,这份代码知道就行了,不需详谈