内存分配器
Python中,当要分配内存时,不单纯使用malloc/free,而是在其基础上堆放三个独立的分层,有效的进行分配。
举个栗子:c语言中申请一片空间就需要使用malloc当释放这个空间的时候就要使用free。Python把这一操作放在最底层也就是0层来实现。那么0层之上是用来做什么呢?我们可能遇到过在python里新建了a=5,b=5两个对象,但是这两个对象id是一样的。这就是Python内存分配器的特点之一,它可以不使用malloc申请空间,而是把对象池里已有的对象返回给你。当这个数字足够大比如说a=560000,b=560000,这时候就会发现两个对象的id是不一样的。这其实就是用了0层的malloc了。
Python分配器分层
第0层往下是OS的功能。-2是隐含和机器的物理性相关联的部分,OS的虚拟内存管理器负责这部分功能。-1层是与机器实际进行交互的部分,OS会执行这部分功能。
第3层到第0层调用了一些具有代表性的函数,下图示:以生成字典对象为例
我们可以看到,生成字典它从第三层的分配器,一直走,层层调用。直到使用malloc申请空间。
第零层--通用的基础分配器
以Linux为例,第0层就是指glibc的malloc()。对系统内存进行申请。
Python中并不是在生成所有对象时都去调用malloc(),而是根据要分配的内存大小来改变分配方法。申请的内存大小如果大于256字节(源码中规定为256,可查看源码),就是用malloc();如果小于256就是用1层或者2层。
Objects/obmalloc.c
#define SMALL_REQUEST_THRESHOLD 256
第一层--低级内存分配器
Python中的对象基本上都是小于等于256字节的,并且几乎都是产生后马上就要被废弃的对象。
在这种情况下,如过逐次从0层分配器开始,就会频繁的malloc和free。效率上会有折扣。
因此再分配小对象时,Python内部就会有特殊的处理。实际上执行这一过程的正是第一层和第二层内存分配器。当需要分配小于等于256字节的对象时,就利用第一层的内存分配器,在这一层会事先从第0层开始迅速保留内存空间,将其蓄积起来。第一层的作用就是管理蓄积的空间。(其实就是一个对象池,上层对它进行管理,以应对频繁产生又释放的对象。)
内存结构
根据管理的空间不同,他们各自的叫法也不同。他们之间的关系式包含关系。
最小的单位为block,最终给申请者的就是block的地址。
比他大的是pool(pool包含block)。
最大的是arena(arena包含pool)。
arena > pool >block。为避免频繁的嗲用malloc和free,第0层分配器会以最大的单位arena来保留内存。pool是用于有效管理空的block的。
arena
Objects/objmalloc.c
struct arena_object {
/* malloc后的arena的地址*/
uptr address;
/* 将arena的地址用于给pool使用而对齐的地址 */
block* pool_address;
/* 此arena中空闲的pool数量 */
uint nfreepools;
/* 此arena中pool的总数 */
uint ntotalpools;
/* 连接空闲pool的单向链表 */
struct pool_header* freepools;
/* 稍后说明 */
struct arena_object* nextarena;
struct arena_object* prevarena;
};
以上就是 arena_object结构体,它管理者arena。
arena_object的成员address保存的是使用第0层内存分配器分配的arena地址。arena大小固定为256k。此大小也是宏定义好的。
arena_object的成员pool_address中保存的是arena里开头的pool地址。这里有个问题,为什么除了域address之外还要一个pool地址呢?arena的地址和其中第一个的pool地址应该是一致的呀!这是因为我们用到了系统中的页,为了使用页的功能,所以要和页对齐才导致arena地址不是第一个pool地址。(之后又详细说明)
arena_object还承担着保持被分配的pool的数量,将空pool连接到单向链表的功能。
此外arena_object被数组arenas管理。
Objects/obmalloc.c
/* 将arena_object作为元素的数组 */
static struct arena_object* arenas = NULL;
/* arenas的元素数量 */
static uint maxarenas = 0;