【项目日记(六)】第二层: 中心缓存的具体实现(下)

简介: 【项目日记(六)】第二层: 中心缓存的具体实现(下)

1. 前言

本篇文章在上一篇文章的基础

上,对中心缓存的释放内存做补充

上一篇: 中心缓存具体实现(上)

本章重点:

本篇文章着重讲解中心缓存这一层
释放内存的全部过程,包括如何在线程
缓存中回收小块儿内存,以及如何将
自己的大块span内存还给页缓存


2. 中心缓存回收/还回内存的细节

  • 第一步: 当线程缓存中的哈希桶中小块儿内存的个数大于了该线程缓存一次性向中心缓存中申请的小块儿内存的个数,此时小块儿内存会从线程缓存中还回到中心缓存的span中

细节问题一:

从线程缓存中还回来的小块儿内存是多个,然而中心缓存的桶中可能不止一个span,我们怎么知道哪个小块儿内存对应到哪个span?很明显随意将在小块儿内存还回到任意span肯定是不对的!在上一篇文章中我们提到过如何通过指针得到这块儿空间在程序地址空间上的页号,所以我们可以使用一个unordered_map来存储页号和span的映射关系,当线程缓存还回小块儿内存时,可以通过计算小块儿内存在地址空间的页号来从这个哈希表中找到对应的span,这就是解决了我们的问题!

  • 第二步:当中心缓存中的span结构体中的成员变量:use_count等于0时,代表这个span被分配出去的小块儿内存都已经还回来了,所以此时将这个span整体还给上一级,也就是页缓存

3. 中心缓存回收内存的代码实现

根据上面的讲解,每还回来一个小块儿

内存都要检查一下它对应的span结构

的use_count是否为0,如果为0就要将

整个span结构还给页缓存!

centralcache.h文件:

void CentralCache::ReleaseListToSpans(void* start, size_t size)//一个桶中有多个span,这些内存块儿属于哪个span是不确定的
{
  size_t index = AlignmentRule::Index(size);//计算在哪个桶
  _spanlist[index]._mtx.lock();
  //要想知道这些内存块分别在哪个span,将内存块的地址除8*1024,得到这个内存块属于第几页
  while (start != nullptr)
  {
    void* next = *(void**)start;
    SpanData* span = PageCache::GetInstance()->MapObjectToSpan(start);//利用地址与span的映射函数,找到这个内存块对应的span
    *(void**)start = span->_freeList;//将内存块start头插到span中
    span->_freeList = start;
    span->_useCount--;//还回来一次内存块,就将usecount--,减到0后就又把内存还给pagecache
    if (span->_useCount == 0)//此时说明span切分出去的所有小块儿内存都被还回来了,直接将整个span还给pagecache,pagecache再进行前后页的合并
    {
      _spanlist[index].Erase(span);
      span->_freeList = nullptr;//span中的小块内存已经打乱了,但我知道起始地址和结束地址,可直接置空
      span->_next = nullptr;
      span->_prev = nullptr;
      //还回来内存时不用加桶锁了,但是pagecache的整体大锁需要加上
      _spanlist[index]._mtx.unlock();
      PageCache::GetInstance()->_mtx.lock();
      PageCache::GetInstance()->ReleaseSpanToPageCache(span);
      PageCache::GetInstance()->_mtx.unlock();
      _spanlist[index]._mtx.lock();
    }
    start = next;
  }
  _spanlist[index]._mtx.unlock();
}

注:对于代码的解释都在注释中
并且ReleaseSpanToPageCache函数
是页缓存需要实现的,这里暂时放一放


4. 对于页号与span映射的代码补充

由于这份代码是在pagecache中存放的并且页缓存还没有具体解释,所以看不懂没关系,把页缓存部分学完就都明白了.再一个,存储页号和span的映射关系的哈希表是存储在页缓存中的!因为不止在中心缓存中会使用到这种映射关系,在页缓存时同样页面临相同的问题,所以将它放在了最上层的页缓存中

pagecache.h文件中:

std::unordered_map<PAGE_ID, SpanData*> _idSpanMap;//存储页号和桶中对应的span的映射,解决换回来的内存对应哪个span的问题
//给我一个地址,返回这个地址对应的span
SpanData* PageCache::MapObjectToSpan(void* obj)
{
  PAGE_ID pageId = (PAGE_ID)obj >> PAGE_SHIFT;//将地址右移13位就算出了页号
  std::unique_lock<std::mutex> lock(_mtx);//加锁
  auto ret = _idSpanMap.find(pageId);
  if (ret != _idSpanMap.end())//若找到了对应的页号,就返回对应的span
    return _idSpanMap[pageId];
  else assert(false);//没找到,证明前面的代码有问题
  assert(ret != nullptr);
  return ret;
}

5. 总结

中心缓存这一层的所有内容已经讲解完毕,很巧妙的是,中心缓存使用的是桶锁,只有两个不同的线程同时进入到同一个桶中才会有锁竞争问题,这也是这个项目比较快的原因之一.总的来说,中心缓存的作用是承上启下,负责给线程缓存分配切分好的小块儿内存,以及从线程缓存中回收内存.并且它也会向页缓存申请大块儿内存,并且会在合适的时候将大块儿内存还回去,方便页缓存结构进行内存合并!


🔎 下期预告:页缓存的具体实现🔍


相关文章
|
1月前
|
缓存 开发框架 移动开发
uni-app:下载使用uni&创建项目&和小程序链接&数据缓存&小程序打包 (一)
uni-app 是一个跨平台的开发框架,它允许开发者使用 Vue.js 来构建应用程序,并能够同时发布到多个平台,如微信小程序、支付宝小程序、H5、App(通过DCloud的打包服务)等。uni-app 的目标是通过统一的代码库,简化多平台开发过程,提高开发效率。 在这一部分中,我们将逐步介绍如何下载和使用uni-app、创建一个新的项目、如何将项目链接到小程序,以及实现数据缓存的基本方法。
|
2月前
|
缓存 NoSQL Java
瑞吉外卖项目笔记+踩坑2——缓存、读写分离优化
缓存菜品、套餐数据、mysql主从复制实现读写分离、前后端分离
瑞吉外卖项目笔记+踩坑2——缓存、读写分离优化
|
3月前
|
缓存 开发框架 .NET
看看 Asp.net core Webapi 项目如何优雅地使用内存缓存
看看 Asp.net core Webapi 项目如何优雅地使用内存缓存
|
3月前
|
存储 缓存 开发框架
看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存
看看 Asp.net core Webapi 项目如何优雅地使用分布式缓存
|
5月前
|
敏捷开发 缓存 测试技术
阿里云云效产品使用问题之构建Vue3项目,怎么让node_modules缓存下来
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
6月前
|
缓存
【项目日记(七)】第三层: 页缓存的具体实现(上)
【项目日记(七)】第三层: 页缓存的具体实现(上)
|
6月前
|
缓存
【项目日记(八)】第三层: 页缓存的具体实现(下)
【项目日记(八)】第三层: 页缓存的具体实现(下)
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
74 6
|
7天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
8天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构

热门文章

最新文章