CPython/list 动态数组

用预留容量和缩容阈值避免列表反复搬迁

CPython 的 list 把对象指针连续存储,在扩容时适当预留空间,并在占用低于一半前避免缩容。

v3.12.0内存管理核验 2026-10-09
相关概念

工程问题#

若每次 append 和 pop 都精确调整底层空间,长度在边界附近变化时就会反复调用分配器,长序列追加也可能不断搬迁已有指针。

实际实现#

list_resize 在新长度介于当前容量的一半与容量之间时只改逻辑长度。需要重分配时,它按 newsize 加约八分之一和常数余量计算容量并对齐;批量增长过大时减少额外预留,零长度特殊处理。

处理流程#

  1. 比较新长度与当前已分配容量
  2. 仍在半容量到满容量之间时,保留底层存储,只改长度
  3. 否则计算带余量且按四个指针槽对齐的新容量
  4. 对批量增长和空列表作调整,重分配后更新指针、长度与容量

源码片段#

固定版本 · 原文摘录
Objects/listobject.cv3.12.0 · L51–L59
查看原文件
51    /* Bypass realloc() when a previous overallocation is large enough52       to accommodate the newsize.  If the newsize falls lower than half53       the allocated size, then proceed with the realloc() to shrink the list.54    */55    if (allocated >= newsize && newsize >= (allocated >> 1)) {56        assert(self->ob_item != NULL || newsize == 0);57        Py_SET_SIZE(self, newsize);58        return 0;59    }
list_resize capacity reuse

容量足够且新长度不低于容量一半时,直接更新长度并返回;删除一个元素并不必然缩容。

Objects/listobject.cv3.12.0 · L71–L82
查看原文件
71    new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3;72    /* Do not overallocate if the new size is closer to overallocated size73     * than to the old size.74     */75    if (newsize - Py_SIZE(self) > (Py_ssize_t)(new_allocated - newsize))76        new_allocated = ((size_t)newsize + 3) & ~(size_t)3;7778    if (newsize == 0)79        new_allocated = 0;80    if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) {81        num_allocated_bytes = new_allocated * sizeof(PyObject *);82        items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);
list_resize allocation policy

展示预留容量公式、批量增长抑制、空列表处理和重分配入口。后续代码还会检查分配失败,再更新对象字段。

两段位于同一函数,分别展示避免重分配的区间和真正重分配时的容量策略;完整错误处理保留在源文件中。

收益与取舍#

工程分析

带来的收益

  • 单元素连续追加通过预留容量分摊搬迁成本
  • 扩容与缩容使用不同触发区间,减少长度小幅波动造成的反复重分配
  • 批量增长时避免无益地保留过多额外槽位

付出的代价

  • 列表容量可能显著大于当前长度,空间换取较少分配工作
  • 一次扩容仍可能复制 O(n) 个指针;摊还结论不保证每次操作低延迟
  • 连续的是 PyObject 指针数组,并非 Python 对象本体

可比较的选择

每次按长度精确分配闲置空间较少,但频繁追加或边界波动更容易反复重分配。

分块序列或双端队列可减少整体搬迁,但随机访问、局部性和空间布局有不同成本。

版本与适用范围#

对应 CPython v3.12.0 的内部 list_resize,不是 Python 语言规范保证的扩容比例;其他 Python 实现或版本可以采用不同策略。整体复杂度还受引用计数、对象析构和分配器行为影响。

参考来源#

源码、官方文档与原始论文
01
cpython · list_resize

Objects/listobject.c · list_resize

源码v3.12.0

相关实现与比较#

4 个相关案例
nginx·HTTP upstream 配置解析release-1.26.3

nginx 解析 upstream 块时,用 ngx_array_t 连续保存服务器条目;每遇到一条 server 指令便追加一个元素。

附源码片段 · 通信与流量
14 个匹配案例