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

2020年1月30日21:20:28
评论

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实现对于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);
}

它们执行两项操作:

  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
  • 本文原创发布php教程 ,转载请注明出处,感谢您的尊重!
    • 上一篇:在服务器端实现无间断部署Python应用的教程
    • 下一篇:用Python实现换行符转换的脚本的教程

    相关文章

    相关视频

    • 在Django框架中运行Python应用全攻略
    • 在Python的Django框架中创建和使用模版
    • python获取元素在数组中索引号的方法
    • 浅谈python中截取字符函数strip,lstr...
    • 从Python的源码浅要剖析Python的内存管理
    • Python 简介
    • Python 环境搭建
    • Python 中文编码
    • Python 基础语法
    • Python 变量类型

    网友评论

    文明上网理性发言,请遵守 新闻评论服务协议

    我要评论

  • 从Python的源码浅要剖析Python的内存管理
  • 立即提交

    专题推荐

    • 从Python的源码浅要剖析Python的内存管理 独孤九贱-php全栈开发教程

      全栈 100W+

      主讲:Peter-Zhu 轻松幽默、简短易学,非常适合PHP学习入门

    • 从Python的源码浅要剖析Python的内存管理 玉女心经-web前端开发教程

      入门 50W+

      主讲:灭绝师太 由浅入深、明快简洁,非常适合前端学习入门

    • 从Python的源码浅要剖析Python的内存管理 天龙八部-实战开发教程

      实战 80W+

      主讲:西门大官人 思路清晰、严谨规范,适合有一定web编程基础学习

    作者信息
    从Python的源码浅要剖析Python的内存管理

    php教程

    认证0级讲师

    最近文章

    发布技术文章

    • 最新文章
    • 热门排行

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

    • python之禅怎么打出来
    • python怎么学
    • boosting和bootstrap区别
    • python库是什么意思
    • python卸载后怎么也安装不上
    • python安装后怎么不见了
    • python怎么卸载模块
    • python能做什么?是什么?

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

    • pickle库的使用详解
    • Anaconda的新手使用大全
    • python爬虫是什么?为什么把python叫做爬虫?
    • Python微信库:itchat的用法详解
    • 关于python3学习基础知识总结
    • python爬虫是什么
    • 使用Python可以做什么
    • python如何实现可视化热力图

    推荐视频教程

  • 从Python的源码浅要剖析Python的内存管理 javascript初级视频教程
  • 从Python的源码浅要剖析Python的内存管理 jquery 基础视频教程
  • 视频教程分类

    • php视频教程
    • html视频教程
    • css视频教程
    • JS视频教程
    • jQuery视频教程
    • mysql视频教程
    • Linux视频教程
    • Python视频教程
    • 网站首页
    • PHP视频
    • PHP实战

    PHP中文网:独家原创,永久免费的在线php视频教程,php技术学习阵地!

    Copyright 2014-2019 http://www.php.cn/ All Rights Reserved | 皖B2-20150071-9 从Python的源码浅要剖析Python的内存管理 皖公网安备 34010402701654号 免责申明赞助与捐赠

    企鹅博客
    • 本文由 发表于 2020年1月30日21:20:28
    • 转载请务必保留本文链接:https://www.qieseo.com/336060.html

    发表评论