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

高并发内存池(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.xdnf.cn/news/19048.html

相关文章:

  • Node.js的特性
  • 损失函数,及其优化方法
  • JS中的String总结
  • 2002-2020年全国投入产出表数据
  • Python 中的反射机制与动态灵活性
  • 实测阿里图像编辑模型Qwen-Image-Edit:汉字也能无痕修改(附实测案例)
  • react+vite+ts 组件模板
  • DAY-16-数组的常见操作和形状-2025.8.28
  • FISCO-BCOS-Python 模板
  • RAG概念被误用:AI应用落地需回归上下文工程本质
  • 解锁AI“黑匣”:监督、无监督与强化学习探秘
  • 切入高潜市场,抢占行业先机!ES SHOW 2025展位预订火爆,10月28-30日共启增长新蓝海
  • flutter Function和自定义的Callback有什么区别?
  • 自动化三维测量仪工业零件自动外观三维测量-中科米堆CASAIM
  • Linux系统资源分配算法在VPS云服务器调优-性能优化全指南
  • 【DAB收音机】DAB 信号发生器介绍
  • C++源代码批量转码utf8
  • SystemVerilog学习【六】功能覆盖率详解
  • 【动态规划】简单多状态 dp 问题
  • 【lucene】advanceShallow (int target) 与advance(int target)
  • Kea DHCP高危漏洞CVE-2025-40779:单个数据包即可导致服务器崩溃
  • workflow/http_parser源码解密:HTTP解析器的双倍扩容与零拷贝策略
  • R 语言 eulerr 包绘制韦恩图:比例精准
  • 机器学习(讲解)
  • 使用MySQL计算斐波那契数列
  • 开源工具新玩法:cpolar提升Penpot协作流畅度
  • Spark入门:从零到能跑的实战教程
  • 基于Spring Session + Redis + JWT的单点登录实现
  • 在Ubuntu中安装配置MySql Server
  • [p2p-Magnet] docs | HTTP API与Web界面 | 搜索查询引擎