CPython 修复 mimalloc 内存分配器内存泄漏:机制、场景与验证(gh-issue-151065)
2026/9/10 13:44:48 网站建设 项目流程

CPython 修复 mimalloc 内存分配器内存泄漏:机制、场景与验证(gh-issue-151065)

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

导读

CPython 在 3.13 起内置并默认启用 mimalloc 内存分配器,并在 free-threaded(自由线程)构建中将其作为对象与内存分配的唯一默认分配器。gh-issue-151065 对应的一条核心变更记录(Misc/NEWS.d/next/Core_and_Builtins/2026-06-08-05-31-22.gh-issue-151065._o_31F.rst)明确指出:修复了使用 mimalloc 内存分配器时出现的内存泄漏。本文以该变更为核心,结合 CPython 源码中 mimalloc 的集成方式、线程堆(per-thread heap)与 QSBR 延迟释放机制,拆解泄漏可能发生的生命周期节点,并给出可操作的复现、观测与规避手段,帮助开发者理解并排查 free-threaded 构建下的内存问题。

为什么 CPython 要内置 mimalloc

mimalloc 最初由 Daan Leijen 为 Koka 与 Lean 语言的运行时开发,是通用型高性能分配器。根据官方文档 The mimalloc allocator 一节:

  • 与针对 512 字节以内小对象优化的 pymalloc 不同,mimalloc 可处理任意大小的分配;
  • CPython 3.13 起在底层平台支持的情况下集成 mimalloc(对应 Doc/whatsnew/3.13.rst 中"CPython now bundles the mimalloc library by default"的记录);
  • 在 3.15 中,mimalloc 进一步成为PyMem_RawMalloc等原始内存分配的默认分配器,以提升 free-threaded 构建下的性能(见 Doc/whatsnew/3.15.rst 的 Optimizations 一节)。

mimalloc 在 CPython 中的地位决定了它的任何内存生命周期缺陷都会直接影响解释器长期运行时的内存占用,这正是本次泄漏修复的价值所在。

泄漏修复针对的核心场景:free-threaded 构建的多堆模型

从源码结构看,本次修复针对的泄漏与 free-threaded 构建下 mimalloc 的"每线程多堆"模型密切相关。自由线程构建要求每个线程拥有独立的 mimalloc 堆,以在大多数情况下免锁完成分配与释放(见 Doc/c-api/memory.rst 的 mimalloc 章节)。

每个线程四个堆

Include/internal/pycore_mimalloc.h 中定义了堆的分类:

typedef enum { _Py_MIMALLOC_HEAP_MEM = 0, // PyMem_Malloc() and friends _Py_MIMALLOC_HEAP_OBJECT = 1, // non-GC objects _Py_MIMALLOC_HEAP_GC = 2, // GC objects without pre-header _Py_MIMALLOC_HEAP_GC_PRE = 3, // GC objects with pre-header _Py_MIMALLOC_HEAP_COUNT } _Py_mimalloc_heap_id;

其中_Py_MIMALLOC_HEAP_MEM对应PyMem_Malloc()系列接口,其余三个堆分别服务于非 GC 对象、无 pre-header 的 GC 对象与带 pre-header 的 GC 对象。堆的划分意味着一个堆中的空闲内存不能直接用于另一个堆的分配——这是理解"看似空闲却无法复用"的起点。

线程堆的初始化与 QSBR 延迟释放

线程状态绑定时,tstate_mimalloc_bind()在 Python/pystate.c 中完成各堆的初始化:

  • _mi_tld_init()初始化线程本地数据,MEM堆同时充当"backing"堆;
  • 对象堆(OBJECT/GC/GC_PRE)统一开启page_use_qsbr = true,即使用 QSBR(基于静止状态的回收)延迟释放 mimalloc 页;
  • 默认对象分配走_Py_MIMALLOC_HEAP_OBJECT_PyObject_GC_New()等接口会临时切换到对应 GC 堆。

QSBR 的引入是因为 free-threaded 构建中存在无锁读者:一个对象被释放后,其他线程可能仍在无锁地读取其ob_tid与引用计数字段。因此 Doc/howto/free-threading-python.rst 明确说明:QSBR 会在"页内所有块都被释放"与"该页真正被回收(供新分配或归还操作系统)"之间引入延迟。如果某些对象始终被误判为存活,或者页的回收条件长期无法满足,内存就不会归还,宏观上表现为持续增长的内存泄漏。

线程退出:abandoned pool 回收机制

多线程模型下,线程退出时其堆中可能仍有存活对象(被其他线程引用)。此时的处理逻辑位于 Python/pystate.c 的_PyThreadState_ClearMimallocHeaps()

for (Py_ssize_t i = 0; i < _Py_MIMALLOC_HEAP_COUNT; i++) { // Abandon all segments in use by this thread. This pushes them to // a shared pool to later be reclaimed by other threads. _mi_heap_collect_abandon(&tstate_impl->mimalloc.heaps[i]); }

退出线程会把仍在使用的 segment 推入跨解释器隔离的共享池interp->mimalloc.abandoned_pool,定义于 Include/internal/pycore_mimalloc.h),供其他线程后续认领与复用。若该回收链条中任何一个环节(如 abandon 时机、segment 认领、QSBR 静止状态判断)出错,segment 就会滞留在共享池中无法复用,形成泄漏。本次 gh-issue-151065 修复的正是此类场景中的内存泄漏问题,属于"线程与堆生命周期管理"层面的缺陷修复,而非普通应用层的忘记释放。

如何在实践中验证与观测 mimalloc 内存行为

针对本次修复涉及的内存生命周期问题,可以从运行期与构建期两个维度进行观测和验证。

运行期:通过环境变量切换与统计分配器

在默认(非 free-threaded)构建中,可通过 PYTHONMALLOC 环境变量在运行期选择分配器:

  • PYTHONMALLOC=mimalloc:对PYMEM_DOMAIN_MEMPYMEM_DOMAIN_OBJ域使用 mimalloc,PYMEM_DOMAIN_RAW域使用 C 库malloc
  • PYTHONMALLOC=mimalloc_debug:在 mimalloc 之上叠加 debug hooks,用于检测越界写、重复释放、未初始化读取等内存错误;
  • PYTHONMALLOC=debug:保留默认分配器但叠加调试钩子。

另外,设置非空 PYTHONMALLOCSTATS 后,Python 会在每次新建对象 arena 时以及解释器关闭时打印 pymalloc 或 mimalloc(视当前启用者而定)的统计信息,可直接观察各堆的空闲块、页与 segment 分布,是排查"内存是否归还"的快速手段。

注意:free-threaded 构建下仅接受defaultdebugmimallocmimalloc_debug四种取值,pymalloc系列在该构建中不可用。

运行期:调节延迟释放参数

由于 mimalloc 会推迟将释放的内存归还操作系统,Doc/howto/free-threading-python.rst 建议可通过环境变量MIMALLOC_PURGE_DELAY=0缩短延迟、更快归还内存;代价是分配器性能可能下降。在排查疑似泄漏时,可先用该变量区分"延迟归还"与"真正的泄漏"——若设置后 RSS 显著回落,则多半属于延迟释放而非泄漏。

构建期:关闭或强制 mimalloc

configure 选项--without-mimalloc可禁用 mimalloc(默认启用)。但该选项不能与--disable-gil同时使用,因为 free-threaded 构建强制要求 mimalloc 作为PYMEM_DOMAIN_MEMPYMEM_DOMAIN_OBJ域的分配器且不可禁用。因此,free-threaded 用户无法绕过 mimalloc,只能通过升级到包含修复的版本、或借助tracemalloc/PYTHONMALLOCSTATS定位泄漏来源。

定位泄漏来源的推荐流程

  1. PYTHONMALLOCSTATS=1启动程序,对比运行前后的分配统计,确认内存增长是否来自 mimalloc 管理的堆;
  2. 使用tracemalloc追踪 Python 对象层面的分配来源(对象堆),区分"对象泄漏"与"底层 segment 未归还";
  3. 对 free-threaded 构建,重点检查短生命周期线程频繁创建/销毁、对象跨线程共享的场景,因为 abandoned pool 与 QSBR 正是在这些路径上工作;
  4. 确认修复是否生效:git log查看该变更是否已包含在当前版本中,并观察修复后线程反复创建销毁时 RSS 是否保持稳定。

小结

gh-issue-151065 的修复针对 CPython 中 mimalloc 分配器在特定生命周期路径上的内存泄漏,其本质与 free-threaded 构建的每线程多堆、QSBR 延迟释放、线程退出时 segment 移入 abandoned pool 再认领的机制紧密相关。理解 Include/internal/pycore_mimalloc.h 中的堆划分与 Python/pystate.c 中的堆初始化、QSBR 开启与 abandon 回收逻辑,是排查同类内存增长问题的关键;而PYTHONMALLOCPYTHONMALLOCSTATSMIMALLOC_PURGE_DELAY与 tracemalloc 则提供了从现象到根因的完整观测链路。

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询