Spec-Zone.ru › Python 3.8

Управление памятью

Обзор

Управление памятью в Python включает в себя частную кучу, содержащую все объекты и структуры данных Python. Управление этой частной кучей обеспечивается внутренней частью менеджера памяти Python. Менеджер памяти Python имеет разные компоненты, которые обрабатывают различные аспекты динамического управления хранением, такие как совместное использование, сегментация, предварительная выделение или кэширование.

На самом низком уровне, непосредственный аллокатор памяти гарантирует, что в частной куче достаточно места для хранения всех данных, связанных с Python, взаимодействуя с менеджером памяти операционной системы. На вершине непосредственного аллокатора памяти работают несколько аллокаторов, специфичных для объектов, которые работают с той же кучей и реализуют различные политики управления памятью, адаптированные к особенностям каждого типа объектов. Например, целые числа управляются внутри кучи иначе, чем строки, кортежи или словари, потому что целые числа подразумевают разные требования к хранению и компромиссы между скоростью и размером. Таким образом, менеджер памяти Python делегирует часть работы аллокаторам, специфичным для объектов, но гарантирует, что последние работают в рамках частной кучи.

Важно понимать, что управление кучей Python выполняется самим интерпретатором, и пользователь не имеет над этим контроля, даже если он регулярно манипулирует указателями объектов на блоки памяти внутри этой кучи. Выделение памяти кучи для объектов Python и других внутренних буферов выполняется по мере необходимости менеджером памяти Python через функции Python/C API, перечисленные в этом документе.

Чтобы избежать повреждения памяти, разработчики расширений никогда не должны пытаться работать с объектами Python с помощью функций, экспортируемых библиотекой C: malloc(), calloc(), realloc() и free(). Это приведет к смешанным вызовам между аллокатором C и менеджером памяти Python со смертельными последствиями, потому что они реализуют разные алгоритмы и работают с разными кучами. Однако можно безопасно выделять и освобождать блоки памяти с помощью аллокатора библиотеки C для индивидуальных целей, как показано в следующем примере:

PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
...Do some I/O operation involving buf...
res = PyBytes_FromString(buf);
free(buf); /* malloc'ed */
return res;

В этом примере запрос памяти для буфера ввода-вывода обрабатывается аллокатором библиотеки C. Менеджер памяти Python участвует только в выделении объекта байтов, возвращаемого в результате.

Однако в большинстве ситуаций рекомендуется выделять память из кучи Python, поскольку последняя находится под контролем менеджера памяти Python. Например, это требуется, когда интерпретатор расширен новыми типами объектов, написанными на C. Другая причина использования кучи Python заключается в желании информировать менеджер памяти Python о потребностях в памяти модуля расширения. Даже когда запрашиваемая память используется исключительно для внутренних, высокоспециализированных целей, делегирование всех запросов памяти менеджеру памяти Python позволяет интерпретатору иметь более точное представление о своей общей занимаемой памяти. Следовательно, в определенных обстоятельствах менеджер памяти Python может или не может инициировать соответствующие действия, такие как сборка мусора, уплотнение памяти или другие превентивные процедуры. Обратите внимание, что при использовании аллокатора библиотеки C, как показано в предыдущем примере, выделенная память для буфера ввода-вывода полностью выпадает из менеджера памяти Python.

См. также

Переменная среды PYTHONMALLOC может использоваться для настройки аллокаторов памяти, используемых Python.

Переменная среды PYTHONMALLOCSTATS может использоваться для печати статистики аллокатора памяти pymalloc каждый раз при создании новой области аллокатора pymalloc и при завершении работы.

Интерфейс необработанной памяти

Следующие наборы функций являются оболочками для аллокатора системы. Эти функции безопасны для потоков, нет необходимости удерживать блокировку интерпретатора.

Следующий стандартный аллокатор необработанной памяти использует следующие функции: malloc(), calloc(), realloc() и free(); вызывайте malloc(1) (или calloc(1, 1)), когда запрашивается ноль байтов.

Введено в версии 3.4.

void* PyMem_RawMalloc(size_t n)

Выделяет n байтов и возвращает указатель типа void* на выделенную память или NULL, если запрос не удался.

Запрос нуля байтов возвращает отдельный не-NULL указатель, если это возможно, как если бы вместо этого был вызван PyMem_RawMalloc(1). Память не будет инициализирована каким-либо образом.

void* PyMem_RawCalloc(size_t nelem, size_t elsize)

Выделяет nelem элементов, размер каждого из которых в байтах равен elsize, и возвращает указатель типа void* на выделенную память, или NULL, если запрос не удался. Память инициализируется нулями.

Запрос нуля элементов или элементов размером ноль байтов возвращает отдельный не-NULL указатель, если это возможно, как если бы вместо этого был вызван PyMem_RawCalloc(1, 1).

Введено в версии 3.5.

void* PyMem_RawRealloc(void *p, size_t n)

Изменяет размер блока памяти, на который указывает p, на n байтов. Содержимое будет неизменным до минимального значения старого и нового размеров.

Если p равно NULL, вызов эквивалентен PyMem_RawMalloc(n); в противном случае, если n равно нулю, блок памяти изменяет размер, но не освобождается, и возвращенный указатель является не-NULL.

Если p не равно NULL, оно должно было быть возвращено предыдущим вызовом PyMem_RawMalloc(), PyMem_RawRealloc() или PyMem_RawCalloc().

Если запрос не удался, PyMem_RawRealloc() возвращает NULL и p остается допустимым указателем на предыдущую область памяти.

void PyMem_RawFree(void *p)

Освобождает блок памяти, на который указывает p, который должен был быть возвращен предыдущим вызовом PyMem_RawMalloc(), PyMem_RawRealloc() или PyMem_RawCalloc(). В противном случае, или если PyMem_RawFree(p) был вызван ранее, возникает неопределенное поведение.

Если p равно NULL, операция не выполняется.

END_OF_DOCUMENT_MARKER

Интерфейс памяти

Следующие функции, моделирующие стандарт ANSI C, но определяющие поведение при запросе нулевого количества байтов, доступны для выделения и освобождения памяти из кучи Python.

Используется стандартный аллокатор памяти pymalloc.

Предупреждение

При использовании этих функций необходимо держать GIL.

Изменено в версии 3.6: Стандартный аллокатор теперь pymalloc, а не системный malloc().

void* PyMem_Malloc(size_t n)

Выделяет n байтов и возвращает указатель типа void* на выделенную память или NULL в случае неудачи.

Запрос нулевого количества байтов возвращает отдельный не-NULL указатель, если это возможно, как если бы была вызвана функция PyMem_Malloc(1). Память не будет инициализирована.

void* PyMem_Calloc(size_t nelem, size_t elsize)

Выделяет nelem элементов, каждый размером elsize байтов, и возвращает указатель типа void* на выделенную память или NULL в случае неудачи. Память инициализируется нулями.

Запрос нулевого количества элементов или элементов нулевого размера возвращает отдельный не-NULL указатель, если это возможно, как если бы была вызвана функция PyMem_Calloc(1, 1).

Добавлена в версии 3.5.

void* PyMem_Realloc(void *p, size_t n)

Изменяет размер блока памяти, на который указывает p, на n байтов. Содержимое будет неизменным до минимального размера из старого и нового.

Если p равно NULL, вызов эквивалентен PyMem_Malloc(n); в противном случае, если n равно нулю, размер блока памяти изменяется, но он не освобождается, и возвращаемый указатель не-NULL.

Если p не равно NULL, то он должен был быть возвращен предыдущим вызовом функций PyMem_Malloc(), PyMem_Realloc() или PyMem_Calloc().

Если запрос неудачен, PyMem_Realloc() возвращает NULL, и p остается валидным указателем на предыдущий участок памяти.

void PyMem_Free(void *p)

Освобождает блок памяти, на который указывает p, который должен был быть возвращен предыдущим вызовом функций PyMem_Malloc(), PyMem_Realloc() или PyMem_Calloc(). В противном случае или если PyMem_Free(p) была вызвана ранее, происходит неопределённое поведение.

Если p равно NULL, никакой операции не выполняется.

Для удобства предоставляются следующие макросы, ориентированные на типы. Обратите внимание, что TYPE относится к любому типу C.

TYPE* PyMem_New(TYPE, size_t n)

Аналогично PyMem_Malloc(), но выделяет (n * sizeof(TYPE)) байта памяти. Возвращает указатель, преобразованный к типу TYPE*. Память не будет инициализирована.

TYPE* PyMem_Resize(void *p, TYPE, size_t n)

Аналогично PyMem_Realloc(), но размер блока памяти изменяется на (n * sizeof(TYPE)) байтов. Возвращает указатель, преобразованный к типу TYPE*. При возврате p будет указывать на новую область памяти, или NULL в случае ошибки.

Это макрос препроцессора C; p всегда переприсваивается. Сохраните исходное значение p, чтобы избежать потери памяти при обработке ошибок.

void PyMem_Del(void *p)

Аналогично PyMem_Free().

Кроме того, для вызова аллокатора памяти Python напрямую, без участия функций C API, приведенных выше, предоставляются следующие наборы макросов. Однако обратите внимание, что их использование не сохраняет двоичную совместимость между версиями Python и поэтому устарело в модулях расширения.

  • PyMem_MALLOC(size)
  • PyMem_NEW(type, size)
  • PyMem_REALLOC(ptr, size)
  • PyMem_RESIZE(ptr, type, size)
  • PyMem_FREE(ptr)
  • PyMem_DEL(ptr)

Аллокаторы объектов

Следующие функции, моделирующие стандарт ANSI C, но определяющие поведение при запросе нулевого количества байтов, доступны для выделения и освобождения памяти из кучи Python.

Используется стандартный аллокатор объектов pymalloc.

Предупреждение

При использовании этих функций необходимо держать GIL.

void* PyObject_Malloc(size_t n)

Выделяет n байтов и возвращает указатель типа void* на выделенную память или NULL в случае неудачи.

Запрос нулевого количества байтов возвращает отдельный не-NULL указатель, если это возможно, как если бы была вызвана функция PyObject_Malloc(1). Память не будет инициализирована.

void* PyObject_Calloc(size_t nelem, size_t elsize)

Выделяет nelem элементов, каждый размером elsize байтов, и возвращает указатель типа void* на выделенную память или NULL в случае неудачи. Память инициализируется нулями.

Запрос нулевого количества элементов или элементов нулевого размера возвращает отдельный не-NULL указатель, если это возможно, как если бы была вызвана функция PyObject_Calloc(1, 1).

Добавлена в версии 3.5.

void* PyObject_Realloc(void *p, size_t n)

Изменяет размер блока памяти, на который указывает p, на n байтов. Содержимое будет неизменным до минимального размера из старого и нового.

Если p равно NULL, вызов эквивалентен PyObject_Malloc(n); в противном случае, если n равно нулю, размер блока памяти изменяется, но он не освобождается, и возвращаемый указатель не-NULL.

Если p не равно NULL, то он должен был быть возвращен предыдущим вызовом функций PyObject_Malloc(), PyObject_Realloc() или PyObject_Calloc().

Если запрос неудачен, PyObject_Realloc() возвращает NULL, и p остается валидным указателем на предыдущую область памяти.

void PyObject_Free(void *p)

Освобождает блок памяти, на который указывает p, который должен был быть возвращен предыдущим вызовом функций PyObject_Malloc(), PyObject_Realloc() или PyObject_Calloc(). В противном случае или если PyObject_Free(p) была вызвана ранее, происходит неопределённое поведение.

Если p равно NULL, никакой операции не выполняется.

Стандартные аллокаторы памяти

Стандартные аллокаторы памяти:

Конфигурация

Имя

PyMem_RawMalloc

PyMem_Malloc

PyObject_Malloc

Релизная сборка

"pymalloc"

malloc

pymalloc

pymalloc

Отладочная сборка

"pymalloc_debug"

malloc + отладка

pymalloc + отладка

pymalloc + отладка

Релизная сборка без pymalloc

"malloc"

malloc

malloc

malloc

Отладочная сборка без pymalloc

"malloc_debug"

malloc + отладка

malloc + отладка

malloc + отладка

Легенда:

  • Имя: значение для переменной окружения PYTHONMALLOC
  • malloc: системные аллокаторы из стандартной библиотеки C, функции C: malloc(), calloc(), realloc() и free()
  • pymalloc: аллокатор памяти pymalloc
  • «+ отладка»: с установленными отладочными хуками с помощью PyMem_SetupDebugHooks()
END_OF_DOCUMENT_MARKER ```

Настройка механизмов выделения памяти

Новая в версии 3.4.

PyMemAllocatorEx

Структура, используемая для описания механизма выделения блока памяти. Структура имеет четыре поля:

Поле

Значение

void *ctx

контекст пользователя, переданный в качестве первого аргумента

void* malloc(void *ctx, size_t size)

выделение блока памяти

void* calloc(void *ctx, size_t nelem, size_t elsize)

выделение блока памяти, инициализированного нулями

void* realloc(void *ctx, void *ptr, size_t new_size)

выделение или изменение размера блока памяти

void free(void *ctx, void *ptr)

освобождение блока памяти

Изменено в версии 3.5: Структура PyMemAllocator была переименована в PyMemAllocatorEx и добавлено новое поле calloc.

PyMemAllocatorDomain

Перечисление, используемое для идентификации области выделения памяти. Домены:

PYMEM_DOMAIN_RAW

Функции:

  • PyMem_RawMalloc()
  • PyMem_RawRealloc()
  • PyMem_RawCalloc()
  • PyMem_RawFree()
PYMEM_DOMAIN_MEM

Функции:

  • PyMem_Malloc(),
  • PyMem_Realloc()
  • PyMem_Calloc()
  • PyMem_Free()
PYMEM_DOMAIN_OBJ

Функции:

  • PyObject_Malloc()
  • PyObject_Realloc()
  • PyObject_Calloc()
  • PyObject_Free()
void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

Получить механизм выделения памяти для указанной области.

void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

Установить механизм выделения памяти для указанной области.

Новый механизм должен возвращать distinct non-NULL указатель при запросе нулевого количества байтов.

Для области PYMEM_DOMAIN_RAW механизм выделения должен быть потокобезопасным: блокировка интерпретатора (GIL) не должна быть захвачена при вызове механизма.

Если новый механизм не является обёрткой (не вызывает предыдущий механизм), функция PyMem_SetupDebugHooks() должна быть вызвана для повторной установки отладочных обработчиков поверх нового механизма.

void PyMem_SetupDebugHooks(void)

Настройка обработчиков для обнаружения ошибок в функциях выделения памяти Python.

Нововыделенная память заполняется байтом 0xCD (CLEANBYTE), освобождённая память — байтом 0xDD (DEADBYTE). Блоки памяти окружены «запрещенными байтами» (FORBIDDENBYTE: байт 0xFD).

Проверки во время выполнения:

  • Обнаружение нарушений API, например, PyObject_Free() вызывается для буфера, выделенного PyMem_Malloc()
  • Обнаружение записи перед началом буфера (подтечение буфера)
  • Обнаружение записи после конца буфера (переполнение буфера)
  • Проверка того, что блокировка интерпретатора (GIL) захвачена при вызове функций выделения области PYMEM_DOMAIN_OBJ (например, PyObject_Malloc()) и области PYMEM_DOMAIN_MEM (например, PyMem_Malloc())

При ошибке отладочные обработчики используют модуль tracemalloc для получения стека вызовов, где был выделен блок памяти. Стек вызовов отображается только если tracemalloc отслеживает выделения памяти Python и блок памяти был отслежен.

Эти обработчики устанавливаются по умолчанию, если Python скомпилирован в отладочном режиме. Переменная среды PYTHONMALLOC может использоваться для установки отладочных обработчиков в режиме релиза.

Изменено в версии 3.6: Эта функция теперь также работает с Python, скомпилированным в релизном режиме. При ошибке отладочные обработчики теперь используют tracemalloc для получения стека вызовов, где был выделен блок памяти. Отладочные обработчики теперь также проверяют, захвачена ли блокировка интерпретатора (GIL), когда вызываются функции из областей PYMEM_DOMAIN_OBJ и PYMEM_DOMAIN_MEM.

Изменено в версии 3.8: Шаблоны байтов 0xCB (CLEANBYTE), 0xDB (DEADBYTE) и 0xFB (FORBIDDENBYTE) были заменены на 0xCD, 0xDD и 0xFD для использования тех же значений, что и у отладочных обработчиков CRT Windows malloc() и free().

Механизм выделения pymalloc

Python использует механизм выделения pymalloc, оптимизированный для небольших объектов (не более 512 байтов) с коротким сроком жизни. Он использует отображения памяти, называемые «аренами», с фиксированным размером 256 КБ. Для выделений больше 512 байтов он обращается к PyMem_RawMalloc() и PyMem_RawRealloc().

pymalloc является по умолчанию механизмом выделения для областей PYMEM_DOMAIN_MEM (например, PyMem_Malloc()) и PYMEM_DOMAIN_OBJ (например, PyObject_Malloc()).

Механизм выделения ареной использует следующие функции:

  • VirtualAlloc() и VirtualFree() в Windows,
  • mmap() и munmap() если доступны,
  • malloc() и free() в противном случае.

Настройка механизма выделения памяти Arena pymalloc

Новая в версии 3.4.

PyObjectArenaAllocator

Структура, используемая для описания механизма выделения памяти ареной. Структура имеет три поля:

Поле

Значение

void *ctx

контекст пользователя, переданный в качестве первого аргумента

void* alloc(void *ctx, size_t size)

выделение ареной размером bytes

void free(void *ctx, void *ptr, size_t size)

освобождение ареной

void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)

Получить механизм выделения памяти ареной.

void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)

Установить механизм выделения памяти ареной.

API tracemalloc на C

Новое в версии 3.7.

int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)

Отследить блок выделенной памяти в модуле tracemalloc.

Возвращает 0 при успехе, -1 при ошибке (не удалось выделить память для хранения следа). Возвращает -2 если tracemalloc отключен.

Если блок памяти уже отслеживается, обновит существующий след.

int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)

Отменить отслеживание выделенного блока памяти в модуле tracemalloc. Ничего не делает, если блок не отслеживался.

Возвращает -2 если tracemalloc отключен, в противном случае возвращает 0.

Примеры

Вот пример из раздела Обзор, переписанный так, что буфер ввода-вывода выделяется из кучи Python с использованием первого набора функций:

PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_Malloc */
return res;

Тот же код, использующий набор функций, ориентированных на тип:

PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Del(buf); /* allocated with PyMem_New */
return res;

Обратите внимание, что в двух примерах выше буфер всегда обрабатывается с помощью функций, принадлежащих к одному набору. Действительно, необходимо использовать ту же семейство API памяти для данного блока памяти, чтобы свести к минимуму риск смешивания разных аллокаторов. Следующая последовательность кода содержит две ошибки, одна из которых помечена как критическая, поскольку смешивает два разных аллокатора, работающих с разными кучами.

char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3);  /* Wrong -- should be PyMem_Free() */
free(buf2);       /* Right -- allocated via malloc() */
free(buf1);       /* Fatal -- should be PyMem_Del()  */

Помимо функций, предназначенных для обработки блоков сырой памяти из кучи Python, объекты в Python выделяются и освобождаются с помощью PyObject_New(), PyObject_NewVar() и PyObject_Del().

Эти функции будут объяснены в следующей главе о определении и реализации новых типов объектов на C.

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/c-api/memory.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API