Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Что внутри у питона: как устроена память

Что внутри у питона: как устроена память

Злата Обуховская (Teamlead в Nvidia, евангелист MoscowPython) @ MoscowPython Meetup 63

"В этой части докладов про внутренности питона мы посмотрим, как происходит выделение памяти, как работают счетчики ссылок, кэши объектов и сборка мусора, а также разберемся, причем тут GIL".

Видео: http://www.moscowpython.ru/meetup/63/python-memory/

Moscow Python Meetup
PRO

February 08, 2019
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Что внутри у питона: как
    устроена память
    Злата Обуховская

    View Slide

  2. Краткое содержание предыдущих
    серий

    View Slide

  3. Питон генерирует байткод, а потом
    его исполняет

    View Slide

  4. При исполнении байткода создаются и
    удаляются объекты

    View Slide

  5. char *src = malloc(len);
    char *dst = malloc(len);
    src = scanf("%s", len);
    dst = memcpy(dst, src, len);
    ...
    free(src);
    free(dst);
    def foo():
    src = input()
    dst = src

    View Slide

  6. int
    PyByteArray_Resize(PyObject *self, Py_ssize_t requested_size)
    {
    ...
    sval = PyObject_Malloc(alloc);
    memcpy(sval, PyByteArray_AS_STRING(self),
    Py_MIN((size_t)requested_size, (size_t)Py_SIZE(self)));
    PyObject_Free(obj->ob_bytes);
    obj->ob_bytes = obj->ob_start = sval;

    View Slide

  7. _____ ______ ______ ________
    [ int ] [ dict ] [ list ] ... [ string ] Python core |
    +3 | | |
    _______________________________ | |
    [ Python's object allocator ] | |
    +2 | ####### Object memory ####### | |
    ______________________________________________________________ |
    [ Python's raw memory allocator (PyMem_ API) ] |
    +1 | | |
    __________________________________________________________________
    [ Underlying general-purpose allocator (ex: C library malloc) ]
    0 | |

    View Slide

  8. _____ ______ ______ ________
    [ int ] [ dict ] [ list ] ... [ string ] Python core |
    +3 | | |
    _______________________________ | |
    [ Python's object allocator ] | |
    +2 | ####### Object memory ####### | |
    ______________________________________________________________ |
    [ Python's raw memory allocator (PyMem_ API) ] |
    +1 | | |
    __________________________________________________________________
    [ Underlying general-purpose allocator (ex: C library malloc) ]
    0 | |

    View Slide

  9. Почему память не выделяется
    напрямую?

    View Slide

  10. Гитхаб сына маминой подруги

    View Slide

  11. Чтобы не было фрагментации,
    используют аллокаторы

    View Slide

  12. static void *
    _PyObject_Malloc(void *ctx, size_t nbytes)
    {
    void* ptr;
    if (pymalloc_alloc(ctx, &ptr, nbytes)) {
    _Py_AllocatedBlocks++;
    return ptr;
    }
    ptr = PyMem_RawMalloc(nbytes);
    if (ptr != NULL) {
    _Py_AllocatedBlocks++;
    }
    return ptr;
    }

    View Slide

  13. static int
    pymalloc_alloc(void *ctx, void **ptr_p, size_t nbytes)
    {
    ...
    if (nbytes > SMALL_REQUEST_THRESHOLD) {
    return 0;
    }
    ...
    ...

    View Slide

  14. Внутри питона очень много маленьких
    объектов

    View Slide

  15. View Slide

  16. * 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
    * 41-48 48 5
    * 49-56 56 6
    * 57-64 64 7
    * 65-72 72 8
    * ... ... ...
    * 497-504 504 62
    * 505-512 512 63

    View Slide

  17. View Slide

  18. View Slide

  19. Освобождение памяти

    View Slide

  20. typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt;
    struct _typeobject *ob_type;
    } PyObject;

    View Slide

  21. В объекте хранится счетчик ссылок

    View Slide

  22. TARGET(LOAD_CONST) {
    PyObject *value = GETITEM(consts, oparg);
    Py_INCREF(value);
    PUSH(value);
    FAST_DISPATCH();
    }
    TARGET(POP_TOP) {
    PyObject *value = POP();
    Py_DECREF(value);
    FAST_DISPATCH();
    }

    View Slide

  23. #define Py_DECREF(op)
    do {
    PyObject *_py_decref_tmp = (PyObject *)(op);
    if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA
    --(_py_decref_tmp)->ob_refcnt != 0)
    _Py_CHECK_REFCNT(_py_decref_tmp)
    else
    _Py_Dealloc(_py_decref_tmp);
    } while (0)

    View Slide

  24. void
    PyObject_GC_Del(void *op)
    {
    PyGC_Head *g = AS_GC(op);
    if (IS_TRACKED(op))
    gc_list_remove(g);
    if (_PyRuntime.gc.generations[0].count > 0)
    _PyRuntime.gc.generations[0].count--;
    }
    PyObject_FREE(g);
    }

    View Slide

  25. Проблема: ссылки бывают
    циклическими

    View Slide

  26. Py_DECREF не приведет к удалению
    lst = []
    lst.append(lst)
    del lst

    View Slide

  27. Чтобы решать проблему с циклами,
    используется сборщик мусора

    View Slide

  28. Mark-and-sweep

    View Slide

  29. View Slide

  30. Проблема всех GC на свете:
    Stop the world

    View Slide

  31. В CPython используется generational
    GC

    View Slide

  32. PyObject *
    _PyObject_GC_Alloc(int use_calloc, size_t basicsize)
    {
    PyObject *op;
    PyGC_Head *g;
    size_t size;
    ...
    size = sizeof(PyGC_Head) + basicsize;
    ...
    g = (PyGC_Head *)PyObject_Malloc(size);
    ...
    _PyGCHead_SET_REFS(g, GC_UNTRACKED);
    ...
    op = FROM_GC(g);
    return op;
    }

    View Slide

  33. typedef union _gc_head {
    struct {
    union _gc_head *gc_next;
    union _gc_head *gc_prev;
    Py_ssize_t gc_refs;
    } gc;
    double dummy; /* force worst-case alignment */
    } PyGC_Head;

    View Slide

  34. View Slide

  35. Сборка мусора оптимизирована под
    работу с коротко живущими
    объектами

    View Slide

  36. Что будет с питоном потом

    View Slide