python内存管理及释放_python的内存管理

Python内存管理分为4层,包括基础的库函数分配、Python内存管理器的封装、对象分配和具体对象如dict的分配。内存池在管理中扮演重要角色,主要处理小于512字节的对象分配。内存池包含arena、pool和block,arena通过malloc获取,pool由固定大小的block组成。当申请内存时,会优先从内存池中获取,超过512字节则使用底层的malloc。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

python的内存分配

python的内存分配主要分为4层。在Objects/obmalloc.c中的注释里画出了python的内存分配模型如下。

b5f52b334001

截图来自python代码的注释

其中第0层是库函数的内存分配器,例如malloc。第一层是python对于malloc,calloc,realloc,free所做的封装。第二层是python的object的分配。第三层是具体对象的分配,例如dict等。

第0层

其中最底层的0层是基础的分配器,可以理解为malloc这样的库函数。只有当申请的内存大小大于512字节的时候才会调用malloc。这个阈值在宏SMALL_REQUEST_THRESHOLD中定义,在早先版本的python中是256字节。

例如在Objects/obmalloc.c的_PyObject_Malloc中:

static void *

_PyObject_Malloc(void *ctx, size_t nbytes)

{

void* ptr;

if (pymalloc_alloc(ctx, &ptr, nbytes)) {

return ptr;

}

ptr = PyMem_RawMalloc(nbytes);

if (ptr != NULL) {

raw_allocated_blocks++;

}

return ptr;

}

其中pymalloc_alloc会校验被申请的内存空间大小,如果大于512字节会返回空。而PyMem_RawMalloc就是malloc的一层封装。

第1层

python内存分配器的第一层就是对于malloc,alloc,realloc,free这四个库函数做了一层封装。在python的代码中,对应这四个库函数分别有PyMem_RawMalloc, PyMem_RawCalloc, PyMem_RawRealloc和PyMem_RawFree。以下是PyMem_RawMalloc的实现:

void *

PyMem_RawMalloc(size_t size)

{

if (size > (size_t)PY_SSIZE_T_MAX)

return NULL;

return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size);

}

抛开异常处理之外,可以看到主要就是调用了绑定在_PyMem_Raw之上的一个malloc成员函数。先说结论:这个malloc函数根据宏定义的不同会绑定到_PyObject_Malloc或者是_PyMem_RawMalloc。以下是定义_PyMem_Raw的相关代码:

typedef struct {

/* user context passed as the first argument to the 4 functions */

void *ctx;

/* allocate a memory block */

void* (*malloc) (void *ctx, size_t size);

/* allocate a memory block initialized by zeros */

void* (*calloc) (void *ctx, size_t nelem, size_t elsize);

/* allocate or resize a memory block */

void* (*realloc) (void *ctx, void *ptr, size_t new_size);

/* release a memory block */

void (*free) (void *ctx, void *ptr);

} PyMemAllocatorEx;

#define MALLOC_ALLOC {NULL, _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree}

#ifdef WITH_PYMALLOC

# define PYMALLOC_ALLOC {NULL, _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free}

#endif

#define PYRAW_ALLOC MALLOC_ALLOC

static PyMemAllocatorEx _PyMem_Raw = PYRAW_ALLOC;

所以可以看出,只要声明了WITH_PYMALLOC这个宏,那么对应的四个操作就会使用如下的对应,这四个函数都会在对于分配内存空间不超过512字节的情况下使用python自己的内存池:

_PyObject

malloc

_PyObject_Malloc

calloc

_PyObject_Calloc

realloc

_PyObject_Realloc

free

_PyObject_Free

否则使用的是如下对应,这四个函数只是对于库函数做了一层封装。

_PyMem_Raw

malloc

_PyMem_RawMalloc

calloc

_PyMem_RawCalloc

realloc

_PyMem_RawRealloc

free

_PyMem_RawFree

它们之间的区别是是否使用python的内存管理机制(即只有超过512字节才直接使用malloc等库函数,否则使用内存池分配)。

只有在python2.3的版本之前需要自己手动打开WITH_PYMALLOC这个宏,现在的版本默认使用的是python的内存管理机制。

python内存池

当申请的对象不超过512字节时,对象从python内存池分配空间。

python的内存池同时和第一层以及第二层有关系。具体的层次关系如下图:

b5f52b334001

内存池的层次关系

包含关系很明显,arena包含pool,pool包含block。

只有arena是通过malloc从系统中申请到的内存,它的大小固定都是256k。每次内存池空间不足的时候都通过new_arena()申请新的arena。arena通过Objects/obmalloc.c里的arenas数组管理,如图所示:

b5f52b334001

arenas的结构

另外全局变量unused_arena_objects用于将现在未使用的arena串联进这个单链表。无可用arena的时候,优先从这个链表里寻找内存空间。

对于arena包含的pool,这里需要说明的是,pool并不由arena管理。例如我们需要申请一块内存block的时候,并不是先找到一个arena,然后从它拥有的pool分配一个block。

每个arena中的pool大小都是固定4k字节(一个内存页的大小)。每个pool中所包含的block大小也是固定的。具体的对应关系如下图:

b5f52b334001

申请内存空间大小和block大小的对应

所以如果申请长度为7的空间,就会返回一个大小为8的block。申请大小为68的空间,就会返回一个大小为72的block。

pool在内存管理中由usedpool来组织,它们的关系如下图:

b5f52b334001

Popo截图20191125154543.png

数组中的每个元素都是一个链表头,链表串起了所有有空闲,且block大小相等的pool。很显然,当申请长度为n的空间时,对应的链表一定在usedpools的(n+1)/8的位置。

这里需要注意的是,链表中串联的是待分配的pool。如果一个pool里的所有block都被释放了,它会被从这个链表里移除。这么做的原因应该是尽量保证先使用已经被占用了一部分的内存空间。

对于block就比较简单,只有被占用,被释放和未初始化三个状态。block被释放了之后连接到pool的freeblock链表上。

回顾一下一开始_PyObject_Malloc的代码:

static void *

_PyObject_Malloc(void *ctx, size_t nbytes)

{

void* ptr;

// 从内存池中获取一块内存

if (pymalloc_alloc(ctx, &ptr, nbytes)) {

return ptr;

}

ptr = PyMem_RawMalloc(nbytes);

if (ptr != NULL) {

raw_allocated_blocks++;

}

return ptr;

}

这里的pymalloc_alloc所做的就是第二层的工作,最终返回一个block的地址。

第3层

这一层就是对象自己的分配器。以字典对象的分配PyDict_New为例:

// Objects/dictobject.c

PyObject * PyDict_New(void)

{

// 增加空字典引用 和本文无关

dictkeys_incref(Py_EMPTY_KEYS);

return new_dict(Py_EMPTY_KEYS, empty_values);

}

/* Consumes a reference to the keys object */

static PyObject *

new_dict(PyDictKeysObject *keys, PyObject **values)

{

PyDictObject *mp;

assert(keys != NULL);

if (numfree) {

// free_list是空闲对象的数组,有对象被释放之后优先从空闲对象里分配

mp = free_list[--numfree];

assert (mp != NULL);

assert (Py_TYPE(mp) == &PyDict_Type);

_Py_NewReference((PyObject *)mp);

}

else {

// 分配

mp = PyObject_GC_New(PyDictObject, &PyDict_Type);

if (mp == NULL) {

dictkeys_decref(keys);

if (values != empty_values) {

free_values(values);

}

return NULL;

}

}

mp->ma_keys = keys;

mp->ma_values = values;

mp->ma_used = 0;

mp->ma_version_tag = DICT_NEXT_VERSION();

ASSERT_CONSISTENT(mp);

return (PyObject *)mp;

}

这里调用PyObject_GC_New,最终对应到的是Modules/gcmodule.c里的_PyObject_GC_Alloc。以下是_PyObject_GC_Alloc的代码:

static PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize)

{

struct _gc_runtime_state *state = &_PyRuntime.gc;

PyObject *op;

PyGC_Head *g;

size_t size;

if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head))

return PyErr_NoMemory();

size = sizeof(PyGC_Head) + basicsize;

// 这里是最终获取内存空间的调用

if (use_calloc)

g = (PyGC_Head *)PyObject_Calloc(1, size);

else

g = (PyGC_Head *)PyObject_Malloc(size);

if (g == NULL)

return PyErr_NoMemory();

assert(((uintptr_t)g & 3) == 0); // g must be aligned 4bytes boundary

g->_gc_next = 0;

g->_gc_prev = 0;

// 这部分是将新生成的obj连入gc链表,与本文无关

state->generations[0].count++; /* number of allocated GC objects */

if (state->generations[0].count > state->generations[0].threshold &&

state->enabled &&

state->generations[0].threshold &&

!state->collecting &&

!PyErr_Occurred()) {

state->collecting = 1;

collect_generations(state);

state->collecting = 0;

}

op = FROM_GC(g);

return op;

}

可以看出,排除了将生成的object连入gc链表的操作,本质上就是调用PyObject_Malloc或者PyObject_Calloc获取内存空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值