Управление памятью¶
Обзор¶
Управление памятью в Python включает в себя частную кучу, содержащую все Python объекты и структуры данных. Управление этой частной кучой обеспечивается внутри Менеджера памяти Python. Менеджер памяти Python имеет различные компоненты, которые имеют дело с различными динамическими аспектами управления хранилищем, такими как совместное использование, сегментация, предварительное распределение или кэширование.
На самом низком уровне аллокатор необработанной памяти гарантирует, что в частной куче достаточно места для хранения всех Python-связанных данных, взаимодействуя с менеджером памяти операционной системы. В дополнение к распределителю необработанной памяти несколько объектно-специфических распределителей работают в одной куче и реализуют различные политики управления памятью, адаптированные к особенностям каждого типа объекта. Например, целочисленные объекты управляются в куче иначе, чем строки, кортежи или словари, потому что целые числа подразумевают различные требования к хранению и компромиссы между скоростью и пространством. Таким образом, менеджер памяти Python делегирует часть работы объектно-специфичным аллокаторам, но гарантирует, что последние работают в пределах частной кучи.
Важно понимать, что управление Python кучей выполняется самим интерпретатором и что у пользователя нет контроля над ней, даже если они регулярно манипулируют указателями объектов на блоки памяти внутри этой кучи. Распределение пространства кучи для Python объектов и других внутренних буферов выполняется по запросу менеджером памяти Python через функции API Python/C, перечисленные в этом документе.
Чтобы избежать повреждения памяти, составителям расширений никогда не следует
пытаться оперировать Python объектами с функциями, экспортируемыми
библиотекой C: malloc()
, calloc()
, realloc()
и free()
. Это
приведет к смешанным вызовам между аллокатором C и менеджером памяти
Python с фатальными последствиями, поскольку они реализуют различные
алгоритмы и работают с разными кучами. Однако возможна безопасная аллокация и
освобождения блоков памяти с помощью аллокатора библиотеки C для отдельных
целей, как показано в следующем примере:
PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* для 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;
В этом примере запрос памяти для I/O буфера обрабатывается аллокатором библиотеки C. Диспетчер памяти Python участвует только в выделении байтов объекта, возвращенный в результате.
Однако в большинстве случаев рекомендуется аллоцировать память из Python кучи, поскольку последняя находится под управлением менеджера памяти Python. Например, это требуется, когда интерпретатор расширяется новыми типами объектов, написанными на C. Другой причиной использования Python кучи является желание сообщить менеджеру памяти Python о потребностях модуля расширения в памяти. Даже когда запрашиваемая память используется исключительно для внутренних, высокоспецифичных целей, делегирование всех запросов памяти Python менеджеру памяти приводит к тому, что интерпретатор имеет более точное изображение своего объема памяти в целом. Следовательно, при определенных обстоятельствах менеджер памяти Python может инициировать или не инициировать соответствующие действия, такие как сбор мусора, уплотнение памяти или другие профилактические процедуры. Следует отметить, что при использовании аллокатора библиотеки C, как показано в предыдущем примере, аллоцированная памяти для буфера I/O полностью покидает диспетчер памяти Python.
См.также
Переменную среды PYTHONMALLOC
можно использовать для настройки распределителей
памяти, используемых Python.
Переменную среды PYTHONMALLOCSTATS
можно использовать для печати статистики
pymalloc аллокатора памяти
каждый раз при создании новой арены объекта pymalloc и при завершении работы.
Интерфейс необработанной памяти¶
Следующие наборы функций являются оболочками для системного аллокатора. Эти функции потокобезопасны, GIL не требуется.
В распределителе необработанной памяти по умолчанию используются следующие функции: 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
операция не выполняется.
Интерфейс памяти¶
Следующие наборы функций, смоделированные после стандарта 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 + debug |
pymalloc + debug |
pymalloc + debug |
Релизная сборка, без pymalloc | "malloc" |
malloc |
malloc |
malloc |
Отладочная сборка, без pymalloc | "malloc_debug" |
malloc + debug |
malloc + debug |
malloc + debug |
Легенда:
- Имя: значение для переменной окружения
PYTHONMALLOC
malloc
: системные распределители из стандарта библиотеки C, C функции:malloc()
,calloc()
,realloc()
иfree()
pymalloc
: pymalloc аллокатор памяти- «+ debug»: с отладочными
хуками установленными
PyMem_SetupDebugHooks()
Настройка распределителей памяти¶
Добавлено в версии 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_DOMAIN_MEM
¶ Функции:
-
PYMEM_DOMAIN_OBJ
¶
-
-
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
, чтобы использовать те же значения, что и отладочныеmalloc()
иfree()
Windows CRT.- Обнаружение нарушения API, например:
Аллокатор pymalloc¶
Python содержит аллокатор pymalloc, оптимизированный для небольших
объектов (меньших или равных 512 байтам) с коротким сроком службы. Он использует
сопоставления памяти под названием «арены» с фиксированным размером 256
KiB. Он возвращается к 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)
выделить арену size байт void free(void *ctx, size_t size, void *ptr)
освободить арену
-
void
PyObject_GetArenaAllocator
(PyObjectArenaAllocator *allocator)¶ Взять аллокатор арены.
-
void
PyObject_SetArenaAllocator
(PyObjectArenaAllocator *allocator)¶ Установить аллокатор арены.
tracemalloc C API¶
Добавлено в версии 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
.
Примеры¶
Вот пример из раздела Обзор, перезаписанный таким образом, что буфер I/O аллоцированный из 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); /* выделен с PyMem_Malloc */
return res;
То же код с использованием набора функций, ориентированных на типы:
PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* for I/O */
if (buf == NULL)
return PyErr_NoMemory();
/* ...Выполнить некоторую операцию ввода-вывода с использованием buf... */
res = PyBytes_FromString(buf);
PyMem_Del(buf); /* выделен с 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); /* Неправильно -- должен быть PyMem_Free() */
free(buf2); /* Правильно -- выделено через malloc() */
free(buf1); /* Фатальный -- должен быть PyMem_Del() */
Помимо функций, направленных на обработку необработанных блоков памяти из
Python кучи, объекты в Python аллоцируются и освободаются с помощью
PyObject_New()
, PyObject_NewVar()
и PyObject_Del()
.
Они будут объяснены в следующей главе, посвященной определению и внедрению новых типов объектов в C.