news 2026/7/24 21:39:13

高并发内存池 - central cache 结构设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
高并发内存池 - central cache 结构设计

高并发内存池 - central cache 结构设计

项目 gitee 链接: 高并发内存池项目
项目 github 链接: 高并发内存池项目

central cache 也是一个哈希桶结构,并且映射关系与 thread cache 保持一致,但是链接部分不再是自由链表,而是一个名为SpanList的链表结构,链表的存储对象是名为span的大内存块,span中存储着切割好的小内存块链接而成的自由链表,当 thread cache 申请时,便返回的是自由链表的部分。

这里有几点需要注意:

  1. 对于central cache线程与进程均共享一个,所以设计为单例模式
  2. central cache有被多进程并发访问的可能(多个 thread cache 同时没有资源,同时请求),所以要设计

对于锁的位置,需要分析一下,多个 thread cache 可能申请的不是一个桶中的资源,比如一个申请 6KB,一个申请 8KB,所以不能给 central cache 整体上锁,而是只在哈希桶内部上锁,确保桶只能在任意时刻,被一个线程访问。

下面我们深入探索一下,谈谈为何这么设计,而不是只困在应该这么设计:

为何要使用 Span

如果还使用自由链表去设计 central cache 这一层,就会产生一个致命的问题,内存泄露!

假设 central cache 使用自由链表设计,现在这个程序已经运行了很长时间,多线程疯狂申请和释放 8 字节的内存。此时,CentralCache 的 8 字节桶里,有一个超级巨大的自由链表,里面串着 100 万个 8 字节的空闲内存块(总共占用了约 8MB 的物理内存)。

灾难发生了:程序突然进入了低谷期,不再需要这些小内存了。

这时候,你想把这 8MB 的内存还给操作系统(或者借给需要 16 字节内存的桶用),你该怎么做?

  • 面对单纯的自由链表,你绝望了。因为自由链表里的节点是打散的!这 100 万个 8 字节内存块,可能散布在 2000 个不同的物理页(Page)中,每个页里只闲置了一半的内存,另一半还在被其他线程使用。

  • 操作系统的规则是冷酷的:你必须把完整的一页(4KB)或连续的几页原封不动地还给我,我才能回收。你不能把“半页”内存还给我。

  • 结果:你的内存池患上了“内存泄漏”的绝症。那 8MB 内存永远卡在 CentralCache 的巨大链表里,既无法被其他大小的桶使用,也无法还给操作系统,只能白白浪费。

结论:单纯的自由链表只能“借出”内存,很难完整地“收回”内存。

此时的破局之道,便是引入 Span。

Span 申请的是连续的内存页块,当 thread cache 需要内存是,Span 对大块内存进行分割,将分割后的小块内存进行分配,对已经使用的小块内存数进行记录,如果最后使用的内存都已归还,说明此时这块连续的物理内存已经空闲,可以将其打包再还给上层,解决内存泄露外部碎片的问题。

总结:

  1. 实现内存的 “有借有还”(核心目的)

    • ThreadCache为什么不需要 Span? 因为ThreadCache是线程私有的,它的设计哲学是“只管拿,不管还”。它把内存囤在自己手里是为了极致的分配速度。就算内存浪费,也只浪费在当前线程。
    • CentralCache是全局共享的枢纽。它必须承担回收内存、降低系统整体内存水位的责任。Span 是内存回收的最小管理单元。没有 Span,CentralCache就无法把打散的内存重新聚合成完整的“页”还给 OS。
  2. 解决外部碎片问题

    • 如果CentralCache直接用大链表,长时间运行后,物理内存会被切得稀碎(这里空 8 字节,那里空 16 字节),导致程序明明有 100MB 空闲内存,却无法申请一个连续的 1MB 内存块(这就是外部碎片)。
    • 使用Span,内存总是以“连续的页”为单位申请和释放,保证了物理内存的连续性,极大地减少了外部碎片。
  3. 提高分配效率(局部性原理)

    • ThreadCache来进货时,CentralCache会优先从同一个Span里连续切出多个小块给它。
    • 这些小块在物理内存上是紧挨着的。当 CPU 去访问这些内存时,能极大地提高CPU Cache(L1/L2缓存)的命中率,从而提升整个程序的性能。如果是单纯的大自由链表,拿出来的内存块在物理地址上可能是东一个西一个,CPU 缓存命中率会很低。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/24 21:38:30

Cpp2IL完整指南:如何分析和理解Unity IL2CPP编译后的应用

Cpp2IL完整指南:如何分析和理解Unity IL2CPP编译后的应用 【免费下载链接】Cpp2IL Work-in-progress tool to reverse unitys IL2CPP toolchain. 项目地址: https://gitcode.com/gh_mirrors/cp/Cpp2IL 你是否曾经面对Unity IL2CPP编译后的GameAssembly.dll感…

作者头像 李华
网站建设 2026/7/24 21:38:20

英雄联盟智能助手Seraphine:免费开源的终极战绩查询与BP辅助工具

英雄联盟智能助手Seraphine:免费开源的终极战绩查询与BP辅助工具 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 你是否厌倦了在英雄联盟对局中手动查询队友战绩?是否希望在BP阶段就能…

作者头像 李华
网站建设 2026/7/24 21:36:11

Betaflight Configurator终极指南:5步打造完美无人机飞控系统

Betaflight Configurator终极指南:5步打造完美无人机飞控系统 【免费下载链接】betaflight-configurator Cross platform configuration and management application for the Betaflight firmware 项目地址: https://gitcode.com/gh_mirrors/be/betaflight-config…

作者头像 李华
网站建设 2026/7/24 21:35:14

HarmonyOS开发实战:小分享-@ohos.net.http 网络请求封装进阶

前言 网络请求封装 是大型应用的必备能力,包括请求拦截器、响应拦截器、错误重试、超时控制等。小分享 App 的模板列表、热门分享等数据需要完善的网络层。本篇讲解进阶网络请求封装。详细 API 可参考 HarmonyOS HTTP 官方文档。 一、请求拦截器 interface Reque…

作者头像 李华
网站建设 2026/7/24 21:34:03

F429-HAL-I2C读取AT24C02(2026/7/24)

目录 一:I2C Init 字段逐个解析 ① Instance — 用哪个 I2C 硬件 ② AddressingMode — 7 位还是 10 位地址 ③ ClockSpeed — I2C 总线速率 ④ DualAddressMode — 双地址模式 ⑤ DutyCycle — 时钟占空比 ⑥ GeneralCallMode — 广播模式 ⑦ NoStretchMode — 时钟拉…

作者头像 李华
网站建设 2026/7/24 21:32:32

ADC12DJ3200 JESD204B接口实战:报警寄存器与高速PCB布局设计

1. 项目概述与核心价值在雷达、卫星通信和高端测试测量领域,我们常常面临一个共同的挑战:如何将模拟世界中的超高频、超宽带信号,稳定、可靠且不失真地“搬运”到数字处理单元(通常是FPGA或ASIC)中。传统的高速并行接口…

作者头像 李华