news 2026/10/10 12:25:22

arena、tcache、size class 三件套:为什么 jemalloc 能同时伺候 Firefox、Redis 和 Rust,5.4 又把这套设计验证了一遍

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
arena、tcache、size class 三件套:为什么 jemalloc 能同时伺候 Firefox、Redis 和 Rust,5.4 又把这套设计验证了一遍

arena、tcache、size class 三件套:为什么 jemalloc 能同时伺候 Firefox、Redis 和 Rust,5.4 又把这套设计验证了一遍

【免费下载链接】jemalloc项目地址: https://gitcode.com/GitHub_Trending/je/jemalloc

2026 年 9 月 17 日,jemalloc 5.4.0 正式发布,这个从 2005 年就成为 FreeBSD libc 默认分配器的老牌项目,又一次在"技术债清理、可移植性、生产稳定性"三个维度上交出了答卷——超过 160 个 commit,几乎全部指向同一件事:让一套诞生于 2006 年的设计,在今天的高并发与多样化负载下依旧站得住。而这套设计的全部精髓,可以压缩成三个词:arena、tcache、size class。

从 Mozilla Firefox 到 Redis,再到 Rust 标准库的前身,三个性格迥异、诉求几乎相反的消费方,不约而同地把自己的内存命运交给了 jemalloc。这不是巧合,而是这三件套恰好分别回答了"并发如何不打架""热点如何不锁""碎片如何不膨胀"这三个终极问题。本文结合 5.4.0 仓库源码,把这三件套拆开看个明白。

一、三个消费方,三种"内存性格"

先看这次发布本身。仓库根目录的 ChangeLog 第 7 行写明:5.4.0 (Sep 17, 2026),包含超过 160 个 commit,重点是重构、bug 修复、测试覆盖与选项清理。而这份 ChangeLog 的另一头,是项目 20 年的生态史。

  • Firefox:浏览器是多线程 + 长生命周期的极端样本。标签页、DOM、渲染线程在不停分配和释放几十到几百字节的对象,任何一个"慢慢漏"的分配器都会让浏览器在几天后吃掉整机内存。jemalloc 自 Firefox 3.5 起就作为默认分配器嵌入,是"碎片敏感型"应用的鼻祖级客户。
  • Redis:单线程事件循环,却要处理海量的小对象 KV。每个键值对可能只有几十到几百字节,分配频率极高。Redis 从早期版本起就默认编译链接 jemalloc(源码树的deps/jemalloc即是证据),看中的正是它对 128、160 字节这类小 size class 的精细切分——社区里流传最广的例子就是"130 字节的对象被放进 160 字节的单元"。
  • Rust:Rust 标准库在 1.32 之前把 jemalloc 作为内置默认分配器,后来虽切换为系统分配器,但 jemalloc 仍是 Rust 生态里最主流的显式选择——jemallocatorcrate、TiKV、GreptimeDB(将其设为默认分配器,并借助其 profiling 能力做内存火焰图)都是实例。Rust 对分配器的诉求是:无 GC 前提下的安全释放、可观测性(堆 profiling、stats)、以及多线程下的确定性延迟。

三者诉求各不相同:Firefox 要"长时间不碎",Redis 要"小对象高效 + 低碎片",Rust 要"并发可控 + 可诊断"。jemalloc 用一个统一框架同时满足,靠的正是三件套的分工。

二、第一件套:arena,把锁从"全局"拆到"每线程主场"

传统分配器(如 glibc ptmalloc)在多线程下最痛的点,是全局堆锁。jemalloc 的第一板斧,就是让每个线程拥有自己的 arena,把竞争面摊薄。

看 include/jemalloc/internal/arena.h 中struct arena_s的定义,一个 arena 自带:

  • atomic_u_t nthreads[2]——记录绑定到此 arena 的应用线程数与内部元数据线程数,这是负载均衡的输入;
  • malloc_mutex_t large_mtx——只保护大对象链表,小对象根本不经过它;
  • pa_shard_t pa_shard——页级分配器 shard,arena 向 OS 要页的通道;
  • 末尾的bin_t all_bins[]——flexible array member,arena 与全部 bin(各 size class 的 slab 管理结构)分配在一起,且JEMALLOC_ALIGNED(CACHELINE)保证缓存行对齐,避免伪共享。

arena 的数量与绑定逻辑在 src/arenas_management.c:narenas_auto个自动 arena 用于"线程 × arena 的自动多路复用",arena_choose_hard()在初始化时为线程挑选当前绑定线程数最少的 arena(arena_nthreads_get(...) < arena_nthreads_get(...)的遍历比较),新线程按需懒创建。每个线程通过 TSD(线程特有数据)持有自己的tsd_arena指针,arena_bind()时还会给线程随机分配每个 size class 的 bin shard 序号,进一步错峰。

arena_choose_impl()(见 include/jemalloc/internal/arena_inlines.h)还体现了两条精细策略:

  • 重入(reentrancy)时无条件退回 arena 0,避免分配器内部自分配死锁;
  • 启用 percpu arena 时,percpu_arena_update()会把线程按 CPU 编号迁移到对应 arena,实现"核级隔离"——这是对"锁竞争"的终极解法:同一个 CPU 上的线程共享 arena 且天然串行,跨核的锁竞争被结构性地消除。5.4.0 新增了"允许通过thread.arena恢复 per-CPU arena 选择"的能力,等于给这套机制加上了运行时可控的开关。

大对象还有独立出路:OVERSIZE_THRESHOLD_DEFAULT定义为8 << 20(8MB),arena_choose_maybe_huge()会把超过阈值的大分配直接扔给专用 huge arena,防止大对象在普通 arena 里造成"空洞"式碎片。

三、第二件套:tcache,线程的"免锁后花园"与其 5.4 进化

arena 解决了"锁的分布",但每次分配还是要进入 arena 的 bin 锁。tcache 的出现,让绝大多数分配连锁都不用碰。

include/jemalloc/internal/tcache.h 把 tcache 拆成tcache_slow_t(慢路径数据)与tcache_s(热路径数据)两部分:慢路径持有与 arena 的关联指针、GC 状态(last_gc_time、next_gc_bin_small/large);热路径就是一张cache_bin_t bins[TCACHE_NBINS_MAX]表——每个 size class 一个 LIFO 栈。而 include/jemalloc/internal/cache_bin.h 的注释说得很清楚:cache_bin 是 tcache 与 arena 通信的机制,tcache 通过向 arena 传入 cache_bin 来批量 refill/flush,arena 则通过cache_bin_array_descriptor反向汇总各线程的统计,arena 完全不需要知道 tcache 的存在——这是依赖倒置做得极其干净的一处设计。

分配热路径见 src/tcache.c 的tcache_alloc_small_hard():cache 为空时向 arena 批量填充,CACHE_BIN_PTR_ARRAY_DECLARE一次声明填充指针数组,减少进入 arena 锁的次数;释放侧tcache_bin_flush_bottom()则是按保留目标只回吐超出部分,其余继续留在线程私有的栈里。

5.4.0 对 tcache 最重要的改动,是"从固定策略到自适应策略"。ChangeLog 的 Incompatible changes 一节原文写道:按"GC 事件之间观察到的实际需求"来调整每个 bin 的 fill 与 retention 目标,取代原先固定的 refill/flush 策略,并因此移除了lg_tcache_nslots_mul、tcache_nslots_small_min/max、tcache_nslots_large等 7 个遗留控制项(对应malloc_conf设置会被静默忽略)。这套自适应算法的载体,是独立的 include/jemalloc/internal/tcache_ncached_target.h:

  • tcache_ncached_fill_after_refill():填充成功就翻倍填充量,快速响应需求上升;
  • tcache_ncached_retain_after_gc():按两次 GC 之间实际消耗量(ncached - low_water)外推保留目标,并留 1/4 headroom;
  • tcache_ncached_retain_after_overflow()与tcache_ncached_fill_after_underuse():溢出或低用时指数收缩,防止缓存空占内存。

tcache_slow_s里的bin_nfill[] / bin_nretain[]数组(每 bin 一个)就是这套状态的落点。翻译成人话:热的应用自动多囤,冷的应用自动少囤,每个 bin 独立决策,不再需要人肉调参。同期修复的还有 TSD 生命周期问题——"先初始化 tcache bins、再标记 cache 启用",杜绝重入引导期分配读到未初始化状态。而上一版 5.3.1 加入的tcache_ncached_max运行时选项被完整保留,成为这套自适应机制之上唯一仍需人工介入的旋钮。

四、第三件套:size class 与 slab,碎片的"精算师"

tcache 只能降低锁开销,内存碎片要靠 size class 体系来治。

include/jemalloc/internal/sc.h 用了整整几十行注释描述这套分级体系:最小的 tiny size class 按 2 的幂逐级递增(从1 << SC_LG_TINY_MIN到 quantum);之后是 pseudo-group(按 quantum 等距排布);再往后是 regular groups——每个 group 覆盖一个 2 的幂区间(base, base*2],组内均匀分布SC_NGROUP(当前恒为 4)个档位,相邻档位间距为delta = base / SC_NGROUP。这样设计的效果是:分配请求的"舍入浪费"被严格限制在每个 group 间距之内,且档位越大的区间间距越大,避免为 8KB 对象舍入 16KB 的浪费。sz_size2index()通过 src/sz.c 里启动期构建的sz_size2index_tab查表完成请求大小 → size class 下标的 O(1) 映射。

size class 落到物理内存,靠的是 bin + slab。include/jemalloc/internal/bin.h 中每个 bin 维护三样东西:slabcur(当前服务分配的 slab)、slabs_nonfull(非满 slab 的最小堆,保证新分配总是优先来自地址最低的旧 slab,提升局部性)、slabs_full。slab 的布局参数在 include/jemalloc/internal/bin_info.h 的bin_info_t里一次性算好:reg_size(region 大小)、slab_size、nregs(一块 slab 能切出多少个 region),并用 bitmap 跟踪每个 region 的占用。同一 size class 的所有 slab 数据只存一份bin_infos[SC_NBINS_MAX],arena 侧只存按 shard 拆分的锁与 slab 指针——"一份只读元数据、N 份竞争热点"的结构,本身就是在向缓存行友好性妥协。

这套体系对 Redis 这类负载的收益最直观:假设请求 130 字节,落在 160 字节档位上,单对象"舍入损耗"约 18%,但换来的是释放后内存能立即被同档位的下一个对象复用,且 slab 整块回收时不会有跨尺寸的"狗牙状"空洞——这正是内存碎片率长期稳定在个位数百分比的前提。5.4.0 顺带修复了 size class 数值计算中的溢出检查("Fix numeric overflow checks in size classes"),让这套查表体系在大页、大地址空间等极端配置下依然可证明安全。

五、5.4 的验证方式:把三件套的"底盘"重写一遍

如果说前四节讲的是"设计没变",那么 5.4.0 真正在做的事是:用一次大规模重构证明这套设计可以被干净地拆解与重组。ChangeLog 的优化清单几乎逐条对应三件套的骨架:

  • 前端模块化:把 arena 管理、初始化、fork 编排、分配分发从巨型的jemalloc.c中拆出,形成src/arenas_management.c、src/jemalloc_fork.c、src/jemalloc_init.c、src/malloc_dispatch.c等独立模块,并"解开 tcache/arena 所有权纠缠、消除头文件循环依赖"——说明 arena 与 tcache 的边界在代码层面被正式固化为依赖方向。
  • OS 抽象层:新增include/jemalloc/internal/os/目录,按posix/linux/freebsd/darwin/windows分平台承载文件 I/O、时间、同步、CPU、虚拟内存、atfork、错误处理等实现(如 include/jemalloc/internal/os/posix/vm.h),分配器核心代码从此不再直接依赖具体平台 API——这是"可移植性"维度的系统性加固。
  • 页分配边界简化:移除过时的pai_t/pai.hvtable 抽象,PAC(页分配与缓存)与 HPA(大页分配)改为直接调用,deferred-work 与 decay 编排从 arena 中迁出——arena 的职责被收窄为"纯粹的分发与统计宿主",三件套的层级关系反而更清晰。
  • 新能力仍围绕 arena 展开:EXTENT_ALLOC_FLAG_PINNED让自定义 extent hook 可以把 HugeTLB 等不可回收映射标记为"钉住",绕过 decay/purge 流水线做优先复用,配套新增stats.pinned、stats.arenas.<i>.extents.<j>.npinned等 mallctl 统计接口——即 arena 的"不可换页内存"从此有了可观测性。
  • 稳定性细节:free/free_sized路径保留errno、修复arena_reset潜在死锁、修复 prof 采样与 guard page 的交互 bug——这些都是在真实生产负载(如 Meta 的大规模验证)中暴露后回填的。

一个值得注意的信号是:这次发布没有改变 arena/tcache/size class 的任何一条主干语义,改动全部发生在"谁拥有什么、谁调用谁、跨平台如何实现"这些支撑层上。这正是对一个成熟设计最苛刻的验证方式——你能否在不改动概念模型的前提下,把实现从零重建一遍。5.4 做到了,且带着 5.3.1 在 Meta 生产环境验证过的多个百分点级系统指标改善("Multiple percent of system-level metric improvements were measured in tested production workloads")继续往前。

结语

回到标题的问题:为什么 jemalloc 能同时伺候 Firefox、Redis 和 Rust?因为它的三件套恰好是一个正交的分解——

  • arena 回答并发:锁不再全局,而是摊到每个线程/每个 CPU;
  • tcache 回答热点:每线程私有的免锁栈,让绝大多数分配连 arena 都不碰;
  • size class + slab 回答碎片:把任意请求精确落到有界浪费的档位里,再以 slab 为单位整进整出。

三者互相咬合:tcache 的每个 cache bin 对应一个 size class,arena 的每个 bin 也是按 size class 组织,bin 的 shard 机制又为 tcache 的并发填充提供了细粒度锁。5.4.0 没有发明新概念,它只是把这套咬合关系在代码层面重新铸造了一遍——模块化、OS 抽象、自适应 tcache、pinned 内存统计,每一项都是对原设计的压力测试。20 年前 Jason Evans 论文里那套为多处理器扩展设计的方案,到今天依然在为三种最挑剔的软件打工,且活得越来越好——这本身就是对"arena + tcache + size class"这套三件套最好的背书。

【免费下载链接】jemalloc项目地址: https://gitcode.com/GitHub_Trending/je/jemalloc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 12:24:59

2026毕业论文AI论文网站排名 适合赶due人的工具都在这

本次毕业论文AI写作软件评测说明 当前大学生毕业论文写作的时间成本与规范要求逐年提升&#xff0c;不少学生选择借助AI工具提升写作效率&#xff0c;但市面工具质量参差不齐&#xff0c;部分工具存在参考文献造假、格式不符合高校要求、合规风险高等问题。本次AI论文写作软件测…

作者头像 李华
网站建设 2026/10/10 12:24:01

Linux断点续传实战:curl -C -、wget -c与rsync可靠下载方案

简介&#xff1a;本资源是一份面向Linux网络编程初学者与进阶开发者的断点续传多线程下载实战代码包&#xff0c;聚焦大文件稳定高效下载这一典型工程问题&#xff0c;适用于网络工具开发、嵌入式下载模块实现及C套接字编程练习场景。压缩包共4个文件&#xff0c;含2个核心C源码…

作者头像 李华
网站建设 2026/10/10 12:22:17

JSP+MySQL宿舍管理系统实训:从跑通到面试避坑指南

简介&#xff1a;这份实训作业资源面向计算机相关专业学生与Java Web初学者&#xff0c;提供一套基于JSP与MySQL的学生宿舍管理系统完整实现&#xff0c;可用于课程设计、毕业实训或自学练手。系统围绕学生信息、宿舍登记、住宿分配与调整、费用管理、在线报修、统计报表及用户…

作者头像 李华
网站建设 2026/10/10 12:22:12

Spring Boot+微信小程序代驾系统:订单状态机与落地避坑指南

简介&#xff1a;围绕微信小程序代驾系统展开的毕业设计论文文档&#xff0c;适合计算机相关专业学生、Java 后端开发者&#xff0c;以及正在完成 Spring Boot 类毕设项目的读者参考。内容以代驾业务为场景&#xff0c;系统阐述从选题背景、需求分析到系统设计、技术选型、模块…

作者头像 李华
网站建设 2026/10/10 12:20:34

一个未达标自媒体项目的完整复盘:从工作分解到风险管理的真实案例

简介&#xff1a;北京邮电大学信息与通信工程学院大二下课程期末论文&#xff0c;以作者真实运营自媒体账号的经历为分析对象&#xff0c;完整梳理了项目管理与经济决策知识的应用过程。正文涵盖项目简介、工作分解结构、成本收益分析、竞争战略、失败原因及风险管理、结语等模…

作者头像 李华
网站建设 2026/10/10 12:19:28

FIDIC银皮书中文版工程化拆解:从PDF到可检索条款库与风险检查清单

简介&#xff1a;FIDIC合同&#xff08;银皮书中文版&#xff09;PDF文档&#xff0c;面向国际工程承包、项目管理及合同管理领域的从业者与学习者&#xff0c;用于查阅EPC交钥匙工程标准合同条款、理解合同签订与执行规范。文档以中文完整呈现银皮书正文&#xff0c;目录结构清…

作者头像 李华