设为首页 收藏本站
查看: 1110|回复: 0

[经验分享] [Python源码学习]之内存管理

[复制链接]

尚未签到

发表于 2017-4-29 13:48:43 | 显示全部楼层 |阅读模式
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 提供了简单的封装:



C



Python函数



Python宏




malloc



PyMem_Malloc



PyMem_MALLOC




realloc



PyMem_Realloc



PyMem_REALLOC




free



PyMem_Free



PyMem_FREE





为什么要这么多次一举:




  • 不同的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.c



PyObject *
_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);
}

它们执行两项操作:




  • 分配内存:PyObject_MALLOC



  • 部分初始化对象: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引入了垃圾收集技术。
这个好复杂啊...


参考






  • http://docs.python.org/py3k/c-api/memory.html



  • http://docs.python.org/py3k/c-api/allocation.html

  • Python源码剖析,陈儒

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-370843-1-1.html 上篇帖子: 创建简单声明式python对象模型 下篇帖子: 使用C语言扩展Python(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表