Управление памятью¶
Обзор¶
Управление памятью в 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;
В этом примере запрос памяти для буфера ввода-вывода обрабатывается распределителем библиотеки C. Менеджер памяти Python участвует только в распределении объекта байтов, возвращаемого в результате.
Однако в большинстве случаев рекомендуется выделять память из кучи Python специально потому, что последняя находится под управлением менеджера памяти Python. Например, это требуется, когда интерпретатор расширяется новыми типами объектов, написанными на C. Другой причиной использования кучи Python является желание сообщить менеджеру памяти Python о потребностях модуля расширения в памяти. Даже когда запрошенная память используется исключительно для внутренних, узкоспециализированных целей, делегирование всех запросов памяти менеджеру памяти Python приводит к тому, что интерпретатор получает более точное изображение своего объема памяти в целом. Следовательно, при определенных обстоятельствах менеджер памяти Python может или не может запускать соответствующие действия, такие как сборка мусора, уплотнение памяти или другие превентивные процедуры. Обратите внимание, что при использовании распределителя библиотеки C, как показано в предыдущем примере, выделенная память для буфера ввода-вывода полностью ускользает от менеджера памяти 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
¶ Enum используется для идентификации домена распределителя. Домены:
-
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 КиБ.
Он возвращается к PyMem_RawMalloc()
и PyMem_RawRealloc()
для
выделений размером более 512 байт.
pymalloc — это распределитель по умолчанию доменов
PYMEM_DOMAIN_MEM
(например, PyMem_Malloc()
) и
PYMEM_DOMAIN_OBJ
(например: PyObject_Malloc()
).
Распределитель арены использует следующие функции:
VirtualAlloc()
иVirtualFree()
в Windows ,mmap()
иmunmap()
, если естьmalloc()
иfree()
в противном случае.
Настройка распределителя арены pymalloc¶
Добавлено в версии 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
.
Примеры¶
Далее пример из раздела Обзор, переписанный так, что буфер ввода- вывода выделяется из кучи Python с использованием первого набора функций:
PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* для I/O */
if (buf == NULL)
return PyErr_NoMemory();
/* ...Выполнить некоторые операции ввода-вывода с использованием buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* выделенный с PyMem_Malloc */
return res;
Тот же код, использующий множество функций, ориентированных на типы:
PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* для 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.