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

高并发内存池(14)- PageCache回收内存

高并发内存池(14)- PageCache回收内存

这段代码是页缓存(PageCache) 中用于释放空闲Span并进行内存合并的关键函数,主要目的是减少内存碎片

函数功能

ReleaseSpanToPageCache完成两个核心任务:

  1. 合并相邻的空闲Span:向前和向后查找可合并的Span
  2. 将合并后的Span重新插入页缓存:更新所有管理信息

代码分段解析

1. 向前合并(合并前面的空闲Span)

while (1)
{PAGE_ID prevId = span->_pageId - 1;  // 前一个页号auto ret = _idSpanMap.find(prevId);// 三种情况停止合并:if (ret == _idSpanMap.end()) break;       // 1. 前面没有Spanif (prevSpan->_isUse == true) break;      // 2. 前面的Span正在使用if (prevSpan->_n + span->_n > NPAGES-1) break; // 3. 合并后会超过最大管理大小// 执行合并span->_pageId = prevSpan->_pageId;  // 更新起始页号span->_n += prevSpan->_n;          // 增加页数_spanLists[prevSpan->_n].Erase(prevSpan);  // 从原位置移除delete prevSpan;                          // 释放被合并的Span
}

示例

现有Span: [pageId=100, n=3]
发现前一个Span: [pageId=99, n=2](空闲)
合并后: [pageId=99, n=5]

2. 向后合并(合并后面的空闲Span)

while (1)
{PAGE_ID nextId = span->_pageId + span->_n;  // 后一个页号auto ret = _idSpanMap.find(nextId);// 三种停止条件(同向前合并)if (ret == _idSpanMap.end()) break;if (nextSpan->_isUse == true) break;if (nextSpan->_n + span->_n > NPAGES-1) break;// 执行合并(只需增加页数,起始页号不变)span->_n += nextSpan->_n;_spanLists[nextSpan->_n].Erase(nextSpan);delete nextSpan;
}

示例

现有Span: [pageId=100, n=3]
发现后一个Span: [pageId=103, n=2](空闲)
合并后: [pageId=100, n=5]

3. 最终处理合并后的Span

_spanLists[span->_n].PushFront(span);  // 按新大小插入对应桶
span->_isUse = false;                 // 标记为空闲// 更新页号映射(首尾页都指向这个Span)
_idSpanMap[span->_pageId] = span;
_idSpanMap[span->_pageId + span->_n - 1] = span;

关键设计思想

1. 内存碎片整理

通过合并相邻空闲Span,将多个小Span合并为一个大Span,从而:

  • 减少外部碎片:提高大块连续内存的可用性
  • 提高内存利用率:避免小碎片无法被利用

2. 分级管理

合并后的Span会根据新的大小_n被放入对应的桶(_spanLists[_n]),保持:

  • 小Span(1-128页)在对应桶中
  • 大Span(>128页)直接由系统管理

3. 映射维护

_idSpanMap[span->_pageId] = span;                  // 首页映射
_idSpanMap[span->_pageId + span->_n - 1] = span;   // 尾页映射

确保通过任意一页都能找到整个Span。


合并过程可视化

假设当前状态:

  • 页号100-104:已使用
  • 页号105-107:空闲SpanA(3页)
  • 页号108-109:空闲SpanB(2页)

合并流程:

  1. 释放一个Span [105-107]
  2. 向后合并发现SpanB [108-109]:
    • 合并为 [105-109](5页)
  3. 向前合并检查页号104(假设正在使用,停止)

最终得到大Span [105-109],放入_spanLists[5]桶。


为什么需要这个函数?

解决内存碎片问题

频繁分配释放会导致:

内存状态: [已用][空闲][已用][空闲][已用]

通过合并:

合并后:   [已用][大空闲块][已用]

提升分配效率

大块连续内存可以:

  • 更快满足大内存请求
  • 减少向操作系统申请的次数

支持内存重用

合并后的Span可以再次被切分用于不同大小的请求。


在内存回收流程中的角色

graph TDA[CentralCache释放Span] --> B[调用ReleaseSpanToPageCache]B --> C{尝试向前合并}C -->|成功| D[更新Span信息]C -->|失败| E{尝试向后合并}E -->|成功| F[更新Span信息]E -->|失败| G[直接插入对应桶]D --> H[更新映射关系]F --> HG --> H

这个函数是内存池保持高效的关键,通过智能的合并策略有效缓解了内存碎片问题。

http://www.dtcms.com/a/355810.html

相关文章:

  • Go 语言常用命令使用与总结
  • 【Agent】AutoAgent: A Fully-Automated and Zero-Code Framework for LLM Agents
  • 从零开始:手写数字识别程序的深度学习实践
  • 《实际项目》空调水系统群控方案
  • TensorFlow 深度学习 | 三种创建模型的 API
  • Promptalot-Midjourney提示词分享平台
  • Java爬虫是什么,如何获取API接口
  • 嵌入式开发学习———Qt软件环境下的C++学习(七)
  • Nginx中`location`路径匹配规则
  • 20250828_学习JumpServer开源堡垒机使用:统一访问入口 + 安全管控 + 操作审计
  • AI翻唱-RVC在线使用-AutoDL
  • 现代数据架构中的核心技术组件解析
  • RPM Spec 文件中 `Provides` 与 `%py_provides` 实现原理及应用场景解析
  • AP化学课程知识点解析学习计划及培训机构推荐
  • 解决pycharm中已经设置python解释器但是terminal中没有变成对应的conda环境
  • 步进电机、直流电机常见问题
  • ASCM-专有云公共云
  • C#写的一键自动测灯带的应用 AI帮写的。
  • 梯度下降,梯度消失,梯度爆炸
  • hintcon2025 Verilog OJ
  • 若依cloud集训总结
  • 对于冯诺依曼体系的理解
  • Linux:信号详解--醍醐灌顶
  • 基于Spring Cloud Gateway构建API网关
  • 第三章:Cesium 矢量数据可视化(点、线、面)
  • Shell脚本(1)
  • 机器学习可解释库Shapash的快速使用教程(五)
  • 全能工程软件 Siemens NX:从设计到制造的全流程解决方案,附安装指南
  • 滑台模组如何实现电子制造精密加工?
  • HVV面经总结(二)