一文浅析内存管理机制

Python开发者 / 2021-04-21 18:43:42

象时,会使用最近释放的对象的内存块,因此其驻留在cpu高速缓存中的概率会大大提高

图片Slab分配器

三. Python内存管理机制

内存管理层次结构

图片Python内存层次结构

  • Layer 0:操作系统提供的内存管理接口,比如malloc,free,python不能干涉这一层
  • Layer 1:封装malloc,free等接口PyMem_API,提供统一的raw memory管理接口,为了可移植性
  • Layer 2:构建了更高抽象层次的内存管理策略(GC藏身之处)
  • Layer 3:对象缓冲池
// 第1层 PyMem_Malloc通过一个宏PyMem_MALLOC实现
// pymem.h
PyAPI_FUNC(void *) PyMem_Malloc(size_t);
PyAPI_FUNC(void *) PyMem_Realloc(size_t);
PyAPI_FUNC(void *) PyMem_Free(size_t);

#define PyMem_MALLOC(n)  ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL\
    : malloc(((n) != 0) ? (n) : 1))

#define PyMem_MALLOC(n)  ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL\
    : realloc(((n) != 0) ? (n) : 1))

#define PyMem_FREE    free

// Type-oriented memory interface 指定类型
#define PyMem_New(type, n) \
 ( ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \
 ( (type*)PyMem_Malloc((n) * sizeof(type))) ) )

#define PyMem_NEW(type, n) \
 ( ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \
 ( (type*)PyMem_MALLOC((n) * sizeof(type))) ) )

小块空间的内存池

Python内存池可视为一个层次结构,自下而上分为四层:block,pool,arena和内存池(概念),其中bock, pool, arena在python中都能找到实体,而内存池是由所有这些组织起来的一个概念。

Python针对小对象(小于256字节)的内存分配采用内存池来进行管理,大对象直接使用标准C的内存分配器malloc

对小对象内存的分配器Python进行了3个等级的抽象,从上至下依次为:ArenaPoolBlock。即,Pool由Block组成,Arena由Pool组成。

Block

block内存大小值被称为size class, 大小为:[8, 16, 24, 32, 40, 48 ... 256],(8*n),内存管理器的最小单元,一个Block存储一个Python对象

// obmalloc.c
// 8字节对齐
#define ALIGNMENT 8
#define ALIGNMENT_SHIFT 3
#define ALIGNMENT_MASK (ALIGNMENT - 1)
// block大小上限为256,超过256KB,则交由第一层的内存管理机制
#define SMALL_REQUEST_THRESHOLD  256
#define NB_SMALL_SIZZE_CLASSES (SMALL_REQUEST_THREASHOLD / ALIGNMENT)
// size class index 转换到 size class
#define INDEX2SIZE(I) (((unit) (I)) + 1) << ALIGMENT_SHIFT)
// sizes class 转换到size class index
size = (uint )(nbytes - 1) >> ALIGMENT_SHIFT;

小于256KB的小块内存分配如下图。

图片Block分配策略

如果申请内存大小为28字节,则PyObject_Malloc从内存池中分配32字节的block,size class index为3的pool(参考上图)。

Pool

Pool为一个双向链表结构,一系列Block组成一个Pool,一个Pool中所有Block大小一样;一个Pool大小通常为4K(一个虚拟/系统内存页的大小)。

一个小对象被销毁后,其内存不会马上归还系统,而是在Pool中被管理着,用于分配给后面申请的内存对象。Pool的三种状态

  • used状态:Pool中至少有一个Block已被使用,且至少还有一个Block未被使用,存在usedpools数组中。
  • full状态:Pool中所有的block都已经被使用,这种状态的Pool在Arena中,但不再Arena的freepools链表中
  • empty状态:Pool中所有的Block都未被使用,处于这个状态的Pool的集合通过其pool_head中的nextpool构成一个链表,表头为arena_object中的freepools
// obmalloc.c
#define SYSTEM_PAGE_SIZE (4 * 1024)
#define SYSTEM_PAGE_SIZE_MASK (SYSTEM_PAGE_SIZE - 1)
// 一个pool大小
#define POOL_SIZE SYSTEM_PAGE_SIZE
#define POOL_SIZE_MASK SYSTEM_PAGE_SIZE_MASK
/*pool for small blocks*/
struct pool_header {
    union {
        block *_padding;
        uint count; }ref;   // 分配的block数量
    block *freeblock;  // 指向pool中可用block的单向链表
    struct pool_header *nextpool;  // 指向下一个
    struct pool_header *prevpool;  // 指向上一个<
热门文章
最新文章
推荐文章