從Python的源碼淺要剖析Python的記憶體管理

來源:互聯網
上載者:User
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.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引入了垃圾收集技術。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.