gpt4 book ai didi

从Python的源码浅要剖析Python的内存管理

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 26 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章从Python的源码浅要剖析Python的内存管理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

Python 的内存管理架构(Objects/obmalloc.c):

复制代码 代码如下:

    _____   ______   ______       ________
   [ int ] [ dict ] [ list ] ... [ string ]       Python core         |
+3 | <----- Object-specific memory -----> | <-- Non-object memory --> |
    _______________________________       |                           |
   [   Python's object allocator   ]      |                           |
+2 | ####### Object memory ####### | <------ Internal buffers ------> |
    ______________________________________________________________    |
   [          Python's raw memory allocator (PyMem_ API)          ]   |
+1 | <----- Python memory (under PyMem manager's control) ------> |   |
    __________________________________________________________________
   [    Underlying general-purpose allocator (ex: C library malloc)   ]
 0 | <------ Virtual memory allocated for the python process -------> |
 

  。

    0. C语言库函数提供的接口 。

    1. PyMem_*家族,是对 C中的 malloc、realloc和free 简单的封装,提供底层的控制接口.

    2. PyObject_* 家族,高级的内存控制接口。     3. 对象类型相关的管理接口 。

PyMem_* 。

PyMem_家族:低级的内存分配接口(low-level memory allocation interfaces) 。

Python 对C中的 malloc、realloc和free 提供了简单的封装:

从Python的源码浅要剖析Python的内存管理

为什么要这么多次一举:

  •     不同的C实现对于malloc(0)产生的结果有会所不同,而PyMem_MALLOC(0)会转成malloc(1).
  •     不用的C实现的malloc与free混用会有潜在的问题。python提供封装可以避免这个问题。
  •         Python提供了宏和函数,但是宏无法避免这个问题,故编写扩展是应避免使用宏

源码:

  Include/pymem.h#define PyMem_MALLOC(n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL              : malloc((n) ? (n) : 1))#define PyMem_REALLOC(p, n) ((size_t)(n) > (size_t)PY_SSIZE_T_MAX ? NULL               : realloc((p), (n) ? (n) : 1))#define PyMem_FREE free  Objects/object.c/* Python's malloc wrappers (see pymem.h) */void *PyMem_Malloc(size_t nbytes){  return PyMem_MALLOC(nbytes);}...

除了对C的简单封装外,Python还提供了4个宏 。

    PyMem_New 和 PyMem_NEW 。

    PyMem_Resize和 PyMem_RESIZE 。

它们可以感知类型的大小 。

#define PyMem_New(type, n)  ( ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL :       ( (type *) PyMem_Malloc((n) * sizeof(type)) ) )#define PyMem_Resize(p, type, n)  ( (p) = ((size_t)(n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL :        (type *) PyMem_Realloc((p), (n) * sizeof(type)) )#define PyMem_Del        PyMem_Free#define PyMem_DEL        PyMem_FREE
以下涉及的一些函数仍旧是函数和宏同时存在,下划线后全是大写字符的是宏,后面不再特别说明。
PyObject_*

PyObject_*家族,是高级的内存控制接口(high-level object memory interfaces).

    注意 。

  •     不要和PyMem_*家族混用!!
  •     除非有特殊的内粗管理要求,否则应该坚持使用PyObject_*

源码 。

  Include/objimpl.h#define PyObject_New(type, typeobj)         ( (type *) _PyObject_New(typeobj) )#define PyObject_NewVar(type, typeobj, n)         ( (type *) _PyObject_NewVar((typeobj), (n)) )  Objects/object.cPyObject *_PyObject_New(PyTypeObject *tp){  PyObject *op;  op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));  if (op == NULL)    return PyErr_NoMemory();  return PyObject_INIT(op, tp);}PyVarObject *_PyObject_NewVar(PyTypeObject *tp, Py_ssize_t nitems){  PyVarObject *op;  const size_t size = _PyObject_VAR_SIZE(tp, nitems);  op = (PyVarObject *) PyObject_MALLOC(size);  if (op == NULL)    return (PyVarObject *)PyErr_NoMemory();  return PyObject_INIT_VAR(op, tp, nitems);}

它们执行两项操作:

  1.     分配内存:PyObject_MALLOC 。

  2.     部分初始化对象:PyObject_INIT和PyObject_INIT_VAR 。

初始化没什么好看到,但是这个MALLOC就有点复杂无比了... PyObject_{Malloc、Free} 。

这个和PyMem_*中的3个可是大不一样了,复杂的厉害! 。

void * PyObject_Malloc(size_t nbytes)void * PyObject_Realloc(void *p, size_t nbytes)void PyObject_Free(void *p)

Python程序运行时频繁地需要创建和销毁小对象,为了避免大量的malloc和free操作,Python使用了内存池的技术.

  •     一系列的 arena(每个管理256KB) 构成一个内存区域的链表
  •     每个 arena 有很多个 pool(每个4KB) 构成
  •     每次内存的申请释放将在一个 pool 内进行

单次申请内存块 。

当申请大小在 1~256 字节之间的内存时,使用内存池(申请0或257字节以上时,将退而使用我们前面提到的PyMem_Malloc).

每次申请时,实际分配的空间将按照某个字节数对齐,下表中为8字节(比如PyObject_Malloc(20)字节将分配24字节).

复制代码 代码如下:

Request in bytes     Size of allocated block      Size class idx
  ----------------------------------------------------------------
         1-8                     8                       0
         9-16                   16                       1
        17-24                   24                       2
        25-32                   32                       3
        33-40                   40                       4
         ...                   ...                     ...
       241-248                 248                      30
       249-256                 256                      31
 
       0, 257 and up: routed to the underlying allocator.
      

  。

这些参数由一些宏进行控制:

#define ALIGNMENT        8        /* must be 2^N *//* Return the number of bytes in size class I, as a uint. */#define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT)#define SMALL_REQUEST_THRESHOLD 256

pool 。

每次申请的内存块都是需要在 pool 中进行分配,一个pool的大小是 4k。由下列宏进行控制:

#define SYSTEM_PAGE_SIZE        (4 * 1024) #define POOL_SIZE               SYSTEM_PAGE_SIZE        /* must be 2^N */ 。

每个pool的头部的定义如下:

struct pool_header {  union { block *_padding;      uint count; } ref;     /* number of allocated blocks  */  block *freeblock;          /* pool's free list head     */  struct pool_header *nextpool;    /* next pool of this size class */  struct pool_header *prevpool;    /* previous pool    ""    */  uint arenaindex;          /* index into arenas of base adr */  uint szidx;             /* block size class index    */  uint nextoffset;          /* bytes to virgin block     */  uint maxnextoffset;         /* largest valid nextoffset   */};

注意,其中有个成员 szidx,对应前面列表中最后一列的 Size class idx。这也说明一个问题:每个 pool 只能分配固定大小的内存块(比如,只分配16字节的块,或者只分配24字节的块...).

要能分配前面列表中各种大小的内存块,必须有多个 pool。同一大小的pool分配完毕,也需要新的pool。多个pool依次构成一个链表 arena 。

多个pool对象使用被称为 arena 的东西进行管理.

struct arena_object {  uptr address;  block* pool_address;  uint nfreepools;  uint ntotalpools;  struct pool_header* freepools;  struct arena_object* nextarena;  struct arena_object* prevarena;};

arean控制的内存的大小由下列宏控制:

#define ARENA_SIZE       (256 << 10)   /* 256KB */

一系列的 arena 构成一个链表。 引用计数与垃圾收集 。

Python中多数对象的生命周期是通过引用计数来控制的,从而实现了内存的动态管理.

但是引用计数有一个致命的问题:循环引用! 。

为了打破循环引用,Python引入了垃圾收集技术.

最后此篇关于从Python的源码浅要剖析Python的内存管理的文章就讲到这里了,如果你想了解更多关于从Python的源码浅要剖析Python的内存管理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com