tcmalloc

关注
义项:Google 的内存分配库
tcmalloc

tcmalloc 是 Google 开发的一种 C/C++ 内存分配器,用于替代 C 标准库和 C++ 标准库自带的 malloc 与 operator new 实现,全称 Thread-Caching Malloc(线程缓存内存分配)。它为高并发、多线程程序设计,通过在每线程或每 CPU 的本地缓存中完成大部分分配与释放,避免频繁加锁,从而降低锁竞争并提升伸缩性;同时通过按大小分类与页级管理减少内存碎片和每个对象的额外开销。Google 内部绝大多数 C++ 程序在生产环境中使用它,其开源版本也是业界常用的高性能分配器之一。

百科 图文
目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

tcmalloc 是 Google 针对 C 语言的 malloc() 与 C++ 的 operator new 提供的定制实现,用于在 Google 自身的 C 和 C++ 代码中完成内存分配,它是对 C 标准库(在 Linux 上通常经由 glibc)与 C++ 标准库所提供分配器的一种替代方案[1][2]。它同时实现 C11、C++11、C++14 和 C++17 标准中的动态内存接口,包括 malloc、calloc、realloc、free 以及 ::operator new、::operator delete 及其数组变体[2]。

它以多线程环境下的高伸缩性为目标,在分配与释放路径上尽量不加锁,并通过采样方式持续输出堆内存的使用信息,便于应用观察自身的内存状况[2][3]。名称中的「TC」指线程缓存(Thread Caching),这是它最初区别于其他分配器的特征,后来名称作为历史沿用下来[2]。

原理

tcmalloc 的结构可分为前端、中端与后端三层:前端是紧邻应用的缓存,负责快速分配与回收;中端负责在缓存不足时补充前端;后端负责向操作系统申请与归还内存[4]。前端缓存有两种形态,默认的每 CPU 缓存为每个逻辑核心维护本地缓存,较早的每线程缓存为每个线程维护本地缓存,两者都使大部分分配与释放无需加锁[2]。

小对象与大于 32K 的对象走不同路径。小对象按大小归入约 170 个大小类,请求的尺寸被向上取整到对应大小类;同尺寸对象由称为 Span 的连续页面区域切分而成,一个 4K 页面可以切成 32 个 128 字节的对象[5]。大对象按页对齐,占用整数个页面,由中央页堆管理,页堆中的空闲链表以页面数量区分,若请求 k 页而只有更大的连续页可用,剩余部分会重新放回相应链表[5]。

小对象分配的快路径依次为:把尺寸映射到大小类,在该线程或该 CPU 对应大小类的空闲链表中查找,若链表非空则直接取走一个对象并返回,整个过程不获取任何锁[5]。若本地链表为空,则从该大小类共享的中央空闲链表批量取出一组对象放入本地缓存;若中央空闲链表也为空,则从中央页分配器申请一段页面,切分成该大小类的对象后放入中央空闲链表[6]。释放对象时通过页号查出其所属的 Span,据此判断是小对象还是大对象,小对象放回当前本地缓存,本地缓存超过预定大小(默认 2MB)时触发回收,把闲置对象移回中央空闲链表[6]。

每 CPU 缓存依赖 Linux 内核提供的可重启序列(restartable sequences, RSEQ)机制。该机制允许一段代码相对其他线程原子地执行,若中途被内核抢占则重新执行,因此访问每 CPU 数据时不需要锁或昂贵原子指令;由于同一核心在同一时刻只运行一个线程,缓存可以在多个交错执行的线程之间共享,且缓存总数受 CPU 数限制,便于用数组而非链表组织元数据[3][7]。在不支持该特性的系统上,tcmalloc 回退到每线程缓存[3]。

$lock/unlock \approx 100\,\text{ns}$(2.8GHz Xeon 上)[5]。

发展历程

Google 于 2005 年将其内存分配器作为「Google Performance Tools」的一部分开源,该项目后来被社区接纳为 gperftools[8]。文献记载 tcmalloc 由 Google 的研究人员开发(Sanjay Ghemawat 与 Paul Menage),并于 2005 年发布[9][10]。开源之后,Google 内部的实现与外部的代码逐渐分道扬镳,外部那份即以 gperftools 之名在社区流传[8]。

2020 年 2 月,Google 公开宣布 TCMalloc 独立仓库,代码本身只包含内存分配器实现,并基于 Abseil 构建;该版本引入了基于可重启序列的无锁每 CPU 缓存、巨页感知的后端,以及利用 C++14 的 sized delete 与 C++17 的过对齐分配等语言特性所做的优化,还提供常开的采样式堆分析[3][8]。

在此前后,Google 围绕数据中心规模的需求持续改造 tcmalloc 的后端。巨页感知的实现 Temeraire 在 2021 年的会议上发表,通过更密集地填充巨页子区域、并在适当时机把空闲巨页整体归还系统,减少 TLB 失效与内存碎片[11]。2024 年发表的表征研究进一步针对缓存层级提出基于使用情况的动态缓存大小、利用硬件拓扑降低核间通信开销等优化,并在生产集群中做全集群 A/B 实验,整体取得约 1.4% 的吞吐提升与 3.4% 的内存占用下降[12][13]。

应用

tcmalloc 被 Google 用于自身生产环境中的绝大多数 C++ 程序,尤其是承担数据中心工作负载的多线程服务[8]。由于它针对多线程小对象分配做了优化、大对象使用自旋锁,对以大量小对象分配为主的服务器程序较为适用,早期观察显示 MySQL 一类程序通过替换默认分配器即可获得性能提升[14]。

在仓库中,tcmalloc 所涉论文与优化案例覆盖了搜索引擎、广告服务、负载均衡器、分布式数据库与机器学习应用等数据中心场景,例如其巨页感知后端曾在 TensorFlow、Spanner 等应用上做评估[15]。tcmalloc 也支持非统一内存访问(NUMA)架构,在该模式下它为每个 NUMA 节点复制一套大小类与页分配器,使分配尽量返回本地内存[7]。

使用方式上,可将二进制以 -ltcmalloc 链接以获得替换 malloc 与 new 的效果,Linux 下也可通过 LD_PRELOAD 对未自行编译的程序生效,但官方认为后者较为棘手、并不特别推荐[16][17]。它与 gperftools 是两个同源项目,后者的代码库除分配器外还包含堆分析器、CPU 分析器与堆检查工具等[8]。

局限

tcmalloc 的性能依赖本地缓存的命中率。其相对优势集中在每线程缓存有效的场合,通常是分配较频繁、对象生命周期偏短的典型 C++ 代码;当访问中央缓存时,由于中央空闲链表与页堆并未分片,缓存未命中场景的伸缩性不如部分同类实现[18]。因此,在部分工作负载下需要调高线程缓存上限,线程长期空闲时也可通过接口把线程标记为空闲,以便回收其占用的缓存[18]。

性能随分配尺寸增大而下降。每线程缓存以 2MB 为阈值触发垃圾回收,分配尺寸越大,回收前能存放的对象越少;在最大分配尺寸约 32K 处存在明显的性能拐点,因为超过这一尺寸的对象不再进入每线程缓存,而是由中央页堆直接分配[19]。

在资源占用方面,本地缓存会滞留一部分内存。为提高分配速度,已释放的内存不一定立即归还系统,tcmalloc 通过专门的周期性释放方法以可配置速率(MB/s)归还内存,这是一种启发式权衡:释放过于积极会增加系统调用与缺页开销,过于保守则让内存闲置[15]。缓存的引入也带来代价,在应用自身对 CPU 缓存压力较大时,分配器的数据结构可能被应用数据挤出缓存,使原本只有十几到二十个周期的快路径退化为访问主存的较长相依延迟[20]。

此外,随线程数量增加,每线程缓存模式需要在线程间挪用缓存额度,活跃线程会从空闲线程处「借走」额度,因此整体上难以同时兼顾每个线程的缓存容量与内存占用[18]。Google 自己的实现基于 Abseil,并且明确不提供 ABI 稳定性[8]。

参见

  • malloc —— tcmalloc 所替代的 C 标准库内存分配接口。

  • glibc —— Linux 上 tcmalloc 最常替代其 malloc 实现的 C 运行库。

  • gperftools —— 与 tcmalloc 同源、由社区维护的 Google 性能工具项目。

  • jemalloc —— 与 tcmalloc 同类的另一款通用内存分配器。

  • 内存碎片 —— tcmalloc 通过大小类与页管理试图缓解的问题。

  • 巨页 —— tcmalloc 后端为降低 TLB 失效而加以利用的机制。

参考资料

  1. TCMalloc [引用日期2026-10-14]
  2. TCMalloc Overview [引用日期2026-10-14]
  3. title: Announcing TCMalloc [引用日期2026-10-14]
  4. 【译】TcMalloc [引用日期2026-10-14]
  5. tcmalloc [引用日期2026-10-14]
  6. tcmalloc [引用日期2026-10-14]
  7. **Evaluation [引用日期2026-10-14]
  8. TCMalloc and gperftools [引用日期2026-10-14]
  9. arxiv.org 上的网页 [引用日期2026-10-14]
  10. [1] Emery D [引用日期2026-10-14]
  11. research.google 上的网页 [引用日期2026-10-14]
  12. acm.org 上的网页 [引用日期2026-10-14]
  13. mit.edu 上的网页 [引用日期2026-10-14]
  14. tcmalloc success [引用日期2026-10-14]
  15. osdi21-hunter(PDF) [引用日期2026-10-14]
  16. README [引用日期2026-10-14]
  17. TCMalloc : Thread-Caching Malloc - The Wayback Machine [引用日期2026-10-14]
  18. TCMalloc : Thread-Caching Malloc [引用日期2026-10-14]
  19. TCMalloc : Thread-Caching Malloc [引用日期2026-10-14]
  20. harvard.edu 上的文件 [引用日期2026-10-14]
词条评价
词条统计

浏览次数:2 次

阅读量:0 次 · 阅读完成量:0 次

最近更新:2026-10-13T21:00:44Z

历史版本

本条目引用的词条
malloc glibc gperftools jemalloc 内存碎片 巨页