当前位置: 首页 > wzjs >正文

网站建设名字专业网站设计网站

网站建设名字,专业网站设计网站,有了域名 做网站,华为商城官网手机版前言:经过前3期的攻坚,我们已完整实现了内存动态申请的核心模块。接下来将进入关键阶段——内存释放机制的理解与实现,这是构建完整 高并发内存池 的最后一块技术拼图。该模块完成后,项目主体架构将基本成型(达90%&…

        前言:经过前3期的攻坚,我们已完整实现了内存动态申请的核心模块。接下来将进入关键阶段——内存释放机制的理解与实现,这是构建完整 高并发内存池 的最后一块技术拼图。该模块完成后,项目主体架构将基本成型(达90%),后续主要聚焦于边界测试和性能调优。欢迎关注本专栏的开发者们持续追踪代码演进,共同探讨高并发内存池的工程实践优化方案。

项目专栏:高并发内存池_敲上瘾的博客-CSDN博客

个人主页:敲上瘾-CSDN博客

目录

​编辑

一、ThreadCache内存释放

二、CentralCache内存回收

三、PageCache内存回收

四、源码


一、ThreadCache内存释放

        在ThreadCache层我们探讨过这样一个问题,某一时段某个线程可能需要大量内存,不断向span桶中申请。而用完后这些内存就被放回它自己的自由链表桶了,导致后续线程再需要向span桶申请内存时资源不足,大部分内存都堆积在这个线程自由链表桶

        为了解决这样的问题,我们需要将部分内存归还给CentralCache。那么,自由链表达到多长时需要放回span桶呢?

        之前在设计应该从span桶取多少内存块比较合适时我们也有过类似的困扰,当时设计了一个慢增长算法,每个自由链表中存了一个_maxSize,这里我们就做简单一点,用_maxSize来判断,即当一个自由链表长度超过一个批量(_maxSize)应该申请的内存时,我们把自由链表的_maxSize个节点还回span桶

首先在class ThreadCache中声明一个函数,用来把要释放的内存截下来送入CentralCache层。

        作用是把list中自由链表的_maxSize个节点截下来,然后送入CentralCache层,因为需要找到对应的哈希桶,所以传入内存块大小。

然后在Deallocate函数(释放内存)中检查是否满足归还span桶的条件,如下:

void ThreadCache::Deallocate(void* obj, size_t bytes)
{int index = SizeClass::Index(bytes);_freelists[index].Push(obj);if (_freelists[index].Size() >= _freelists[index].MaxSize()){size_t size = SizeClass::RoundUp(bytes);ListTooLong(_freelists[index], size);}
}

注:Size用来获取链表长度,这里就不展示了,可以用这两种方法之一:

  • 直接遍历链表并计数。
  • 添加并维护_size成员函数,Size()中直接返回_size。 

因为每次使用Size函数都遍历链表会影响效率,所以这里选用添加_size成员的方法。

接下来实现ListTooLong函数:

void ThreadCache::ListTooLong(FreeList& list, size_t size)
{void* start = nullptr;void* end = nullptr;list.PopRange(start, end, list.MaxSize());//交给CentralCacheCentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

        同样的使用start和end指针去取内存,在class FreeList里封装一个函数PopRange,如果你看懂了之前内存申请的代码,这里的实现就显得很简单,不在一一讲解。代码示例:

//取一段内存
void PopRange(void* start, void* end, int num)
{assert(num <= _size);start = end = _list_head;for (int i = 0; i < num - 1; i++){end = Nextobj(end);}_list_head = Nextobj(end);Nextobj(end) = nullptr;_size -= num;
}

二、CentralCache内存回收

小节目标:ReleaseListToSpans(start, size)的实现

        CentralCache内存回收的主要任务就是把ThreadCache送来的内存连接到span桶中,其次,同样的问题,什么时候送到下一层呢?这个问题我们探讨过,就是span中的所有内存块都回收回来时

        记住:理解为什么要span中的所有内存块回收回来后送入下一层这一点很重要,在下文会讲到。

回收的内存插入到Span的自由链表中

        现在有一个问题:内存块要插入到哪个span?我们知道的size仅能找到它所在的哈希桶,也就是一个span链,要具体到某个span节点是一个很让人头疼的问题。

注意:不能粗鲁地把内存块连接到一个随便找的span,这样的话,属于不同页的内存块就混乱在一起,后面的PageCache就没法玩了!

        追溯到本质(思考内存块从“出生”到现在的这个过程):内存块连接到某个span,实际上是要找到内存块属于那个页号,然后连接到这个页号对应的span中。

        页号成为了它们之间的脐带,内存块地址除以8KB就是它所在的页,而一个span的信息中也能找到它管理着哪些页,只需要做一个页号到span的映射(即哈希表)就能解决问题。页级别的内存管理是在PageCache,我们把哈希映射在PageCache层创建和维护。

在PageCache中添加成员:

  • unordered_map<PAGE_ID, Span*> _idSpanMap;  

        _idSpanMap在哪里填写呢?答:在页分配时。在向系统申请内存和给span分配页前是不能确定页号和span的对应关系的,所以哈希映射是随程序运行的动态填写过程。

        将要被连接到CentralCache层的span,需要把它管理的所有页都映射到span中,因为拆开的内存块能获取到的是一个确定的页号。放回PageCache的span只需要它管理的开始页号和尾页号映射到span,用于span合并时对相邻页的查找。

如下:

Span* PageCache::NewSpan(size_t k)
{assert(k > 0 && k < NPAGES);if (!_spanLists[k].Empty()){return _spanLists[k].PopFront();}for (int i = k + 1; i < NPAGES; i++){//切割页(即页分配)if (!_spanLists[i].Empty()){Span* nSpan = _spanLists[i].PopFront();Span* kSpan = new Span;kSpan->_n = k;//页数nSpan->_n -= k;//填写页号kSpan->_pageId = nSpan->_pageId;nSpan->_pageId += k;//建立映射_idSpanMap[nSpan->_pageId] = nSpan;_idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;for (PAGE_ID i = 0; i < kSpan->_n; i++){_idSpanMap[kSpan->_pageId + i] = kSpan;}_spanLists[nSpan->_n].PushFront(nSpan);kSpan->_isUse = true;//表示span被CentralCache使用,在下文会讲到。return kSpan;}}//向系统申请内存Span* span = new Span;void* ptr = SystemAlloc(NPAGES - 1);span->_pageId = (PAGE_ID)ptr>>PAGE_SHIFT;span->_n = NPAGES - 1;_spanLists[span->_n].PushFront(span);return NewSpan(k);
}

对于使用内存块查找span我们封装一个函数,如:

Span* PageCache::MapObjectToSpan(void* obj)
{PAGE_ID index = (PAGE_ID)obj >> PAGE_SHIFT;auto ret = _idSpanMap.find(index);if (ret == _idSpanMap.end()){assert(false);return nullptr;}else{return ret->second;}
}

接下来我们来实现ReleaseListToSpans(start, size),它的核心工作:

  1. 找到每个内存块对应的span。
  2. 把内存块连接到span的自由链表中。
  3. 判断内存块是否全部回收,如果是,就将span送入PageCache中。

注意:找到哈希桶后需要加桶锁。

        对于第3点,在把span送入PageCache之前,需要把span从CentralCache移除,并把span前后指针指向空,自由链表指向空。其次走到这一步意味着程序将离开CentralCache层进入PageCache层,在此之前把桶锁解了,方便让其他线程使用,然后加锁执行PageCache的内存回收函数,解锁,然后再次申请桶锁

代码示例:

void CentralCache::ReleaseListToSpans(void* start, size_t size)
{void* p  = start;//找到每个p所在Span并连接到自由链表//加桶锁int index = SizeClass::Index(size);_spanLists[index]._mtx.lock();while (p){void* next = Nextobj(p);Span* span = PageCache::GetInstance()->MapObjectToSpan(p);Nextobj(p) = span->_freeList;span->_freeList = p;span->_useCount--;if (span->_useCount == 0){//移出Span链表_spanLists->Erase(span);span->_freeList = nullptr;span->_next = span->_prev = nullptr;//交给PageCache回收_spanLists[index]._mtx.unlock();PageCache::GetInstance()->_pageMtx.lock();PageCache::GetInstance()->ReleaseSpanToPageCache(span);PageCache::GetInstance()->_pageMtx.unlock();_spanLists[index]._mtx.lock();}p = next;}_spanLists[index]._mtx.unlock();
}

内存块全部回收后送入PageCache的作用:

        内存块回收到span后,它在自由链表中的排布已经不像初次从PageCache取出那样是有序的,而是混乱的。等span全部内存块回收后,就可以把它们看做一个整体,尽管内存块的排布是混乱的也没关系,这只是逻辑上的混乱,回到PageCache层后它就是一个大块页。等PageCache再次分页时,内存块次序恢复和虚拟地址一样,增加高速缓存命中率。

三、PageCache内存回收

小节目标:ReleaseSpanToPageCache(span)的实现。

        当PageCache拿到span后不要直接连接到哈希桶里,而把它前后能合并的页都合并在一起组成一个大块的页,然后再连接到哈希桶,这样可以缓解内存外碎片

        注意 是把它前后的页span合并在一起,只有连续页的span才能合并,而不是前后桶的span,前后页的span也无法依据桶来找,要找到管理它前后页的span需要用哈希映射。

        但又会带来一些问题,哈希映射得到的span可能是在CentralCache中,也有可能是在PageCache中,因为我们用的是同一个哈希映射,而合并span,指的是PageCache中的span,要如何解决?

        这里我们选择在span中添加成员变量_isUse来判断span是否分配给了CentralCache,这样就可以把它们区分开。先暂时这样写,后期再做优化。比如一个span的_isUse初始状态为false,分配给CentralCache后设为true,PageCache的回收处理后设为false。

合并条件:

  • 它相邻的页在哈希映射中。
  • 该页不被_CentralCache使用。
  • 两个span的页数相加不超过128。

合并过程:

  • 用来储存合并后结果的span:页号更新为两个span中较小的页号,页数更新为两者的页数和。
  • 另一个span:从PageCache的哈希桶中移除,delete删除,释放空间。

合并结果处理:

  • 按上面逻辑把前后页合并得到的span,_isUse设为false,将其连接到PageCache的哈希桶,然后做页到span的哈希映射。

代码示例:

void PageCache::ReleaseSpanToPageCache(Span* span)
{PAGE_ID n = span->_pageId;//向前合并while (true){PAGE_ID prev = span->_pageId - 1;auto ret = _idSpanMap.find(prev);if (ret != _idSpanMap.end()&&!ret->second->_isUse){if (span->_n + ret->second->_n >= NPAGES)break;span->_pageId = ret->second->_pageId;span->_n += ret->second->_n;_spanLists->Erase(ret->second);delete ret->second;}else break;}//向后合并while (true){PAGE_ID prev = span->_pageId + 1;auto ret = _idSpanMap.find(prev);if (ret != _idSpanMap.end() && !ret->second->_isUse){if (span->_n + ret->second->_n >= NPAGES)break;span->_n += ret->second->_n;_spanLists->Erase(ret->second);delete ret->second;}else break;}span->_isUse = false;_spanLists->PushFront(span);_idSpanMap[span->_pageId] = span;_idSpanMap[span->_pageId + span->_n - 1] = span;
}

四、源码

需要源码的小伙伴到我的gitee上取:

内存释放/PageCache · 敲上瘾/ConcurrentMemoryPool - 码云 - 开源中国

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!74c0781738354c71be3d62e05688fecc.png


文章转载自:

http://KeTpoN8w.gbrps.cn
http://fsEjkBkN.gbrps.cn
http://K6OMsg14.gbrps.cn
http://oOvrWoiJ.gbrps.cn
http://qyBQ94D9.gbrps.cn
http://kAdS4Lqf.gbrps.cn
http://3tInLZzt.gbrps.cn
http://U5xXX7Ne.gbrps.cn
http://nuiaoIjL.gbrps.cn
http://EMR4pIG5.gbrps.cn
http://lajqijbL.gbrps.cn
http://alzYJPte.gbrps.cn
http://lYh54Yte.gbrps.cn
http://e2P5FtkW.gbrps.cn
http://77CbhNtC.gbrps.cn
http://gehuosJ1.gbrps.cn
http://hHF7Zp2Q.gbrps.cn
http://f0YK3yxP.gbrps.cn
http://eVgiklv1.gbrps.cn
http://dMTgYpas.gbrps.cn
http://g8QtAIb4.gbrps.cn
http://ZV3xlJpz.gbrps.cn
http://9X4qICRV.gbrps.cn
http://QtzqmenW.gbrps.cn
http://sdmza3SW.gbrps.cn
http://SQessIcf.gbrps.cn
http://54ImKqhW.gbrps.cn
http://rs4nNuyj.gbrps.cn
http://F7QypG5m.gbrps.cn
http://339eKy0L.gbrps.cn
http://www.dtcms.com/wzjs/628171.html

相关文章:

  • 怎样才能有自己的网站国企网站开发
  • 国展做网站的公司丹阳翼网首页
  • 做网贷网站多少钱重庆公司有哪些
  • 长垣有做网站设计的吗最近发生的新闻事件
  • 免费企业网络推广网站佛山新网站建设
  • 上海人才网官网站首页网络营销策略理论有哪些
  • 网站开发前台与后台的交互seo公司杭州
  • 群网站建设合同锦兴建筑人才招聘平台
  • 网站两侧对联广告图片三优科技 网站开发
  • 网站集群建设参数五莲县网站建设
  • 手机网站建设公司电话咨询做英文网站 是每个单词首字母大写 还是每段落首字母大写
  • 戴尔的网站建设有哪些主要特色有什么好网站做浏览器主页
  • 正规不收费的网站wordpress v2pai
  • 网站建设公司工作岗位说明书电商视觉设计是干什么的
  • 中山网站搜索排名蜂鸟影院高清免费观看
  • 中国建设银行网站太慢了冒险岛cms是什么意思
  • 阿里云网站建设一次付费用织梦做的网站怎样看
  • 做英文简历的网站网站优化工作
  • 邯郸网站优化平台沈阳创造价值网站
  • 南宁市网站电子商务网站建设基础项目实训报告
  • asp.net建立网站司法网站建设与维护 教材
  • 国内ui设计网站前程无忧招聘网
  • 专业外贸网站制作软件开发平台下载
  • 加强思政部网站建设网站模板和源码区别
  • app网站开发住房公积金网站建设项目教程
  • seo外链群发网站朋友做的网站图片不显示
  • 免费网站建设怎样莱芜百度网站建设
  • 做宣传 为什么要做网站那只有做推广才能搜索到网站吗
  • 网站建设实训报告模版源码建站教程
  • 淘宝客建设网站长沙网络营销整合收费