arena 是 Python 内存模型中最高等级的内存管理组件,以 arena 结构管理了一系列大小为 256K 的内存。需要注意的是 arena 结构体与 arena 内存是分离的,而且采用的是 lazy alloc 策略,即预分配 arena 结构体,但仅在实际用到的时候才会真正为 arena 分配 256K 内存。一个 arena 中可以包含的 pool 数量很容易计算,256K/4K = 64 个,实际上为了将 arena 的 pool_address 对齐到分页,很有可能会浪费一个 pool,即一般 arena 只有 63 个 pool。
预分配的 arena 存在于一个连续的数组 arenas,初始化大小为 16,其中的 arena 结构体串联起来,构成 unused_arena_objects 和 usable_arenas 两个链表,分别表示未分配 256K 内存的 arena 结构和已分配内存,但尚有可用 pool 的 arena。这些 arena 会随着系统的内存消耗而逐步从 unused 状态转换为 usable 最终转换为 full 状态,arena 和 pool 一样没有所谓的 full 链表,满负荷的 arena 只是简单的从 arena 链表中剔除而已。
显然区区 16 个 arena 总共只能管理 16 * 256 = 4M 内存,如果此时还有新的内存使用需要,Python 会直接 dobule arenas 数组的尺寸,将新增的 arena 链接到 unused_arena_objects 链表中,以供使用。这个过程会随着系统的内存消耗不断进行下去,arenas 数组的尺寸也会以 2^n 增加,能管理的内存大小也会迅速增加。下面展示了 arenas 被扩展的过程。
早期版本的 Python 的内存占用采用一种贪婪的策略,它假定用户一旦使用了某个数量的内存,那么他很有可能会很快还会使用这么多内存。这使得 Python 的内存占用只增不减(实际上是为了减少内存分配的次数,不是偷懒)。比如你只是将一个很大的文件读入内存一次,那么即使之后这块缓冲区被释放了,Python 也会继续持有巨量内存,直到 Python 解释器退出,这种情况下显然不合适。后来在社区的努力下,Python 在满足一定条件的情况下,会主动释放一些内存,具体的说,当一个 arena 内所有的 pool 都处于闲置状态,那么 arena->address 指向的内存会被 free 掉,还给操作系统,这样就不会出现 Python 长时间非必要的占用大量内存的现象。