Spec-Zone.ru › Python 3.9

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

Обзор

Управление памятью в 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)

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

Новый выделю должен возвращать другой не-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 может использоваться для установки обработчиков отладки для Python, скомпилированного в режиме выпуска.

Изменено в версии 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 КБ. Оно возвращается к PyMem_RawMalloc() и PyMem_RawRealloc() для выделения большего размера, чем 512 байтов.

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

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

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

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

Новая в версии 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)

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

END_OF_DOCUMENT_MARKER

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.9/c-api/memory.html

Spec-Zone.ru

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