Содержание страницы
Управление памятью¶Memory Management
Обзор¶Overview
Управление памятью в 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); /* для ввода-вывода */
if (buf == NULL)
return PyErr_NoMemory();
...Do some I/O operation involving buf...
res = PyBytes_FromString(buf);
free(buf); /* выделено через malloc */
return res;
В этом примере запрос памяти для буфера ввода-вывода обрабатывается аллокатором библиотеки C. Менеджер памяти Python участвует только в выделении строкового объекта, возвращаемого в качестве результата.
Однако в большинстве ситуаций рекомендуется выделять память из Python-кучи, поскольку она находится под управлением менеджера памяти Python. Например, это необходимо, когда интерпретатор расширяется новыми типами объектов, написанными на C. Ещё одна причина использовать Python-кучу – желание информировать менеджер памяти Python о потребностях модуля расширения в памяти. Даже если запрошенная память используется исключительно для внутренних, узкоспециализированных целей, передача всех запросов памяти менеджеру памяти Python позволяет интерпретатору получать более точную картину общего объёма используемой памяти. Как следствие, при определённых обстоятельствах менеджер памяти Python может запускать или не запускать соответствующие действия, такие как сборка мусора, уплотнение памяти или другие профилактические процедуры. Обратите внимание: при использовании аллокатора из библиотеки C, как показано в предыдущем примере, выделенная память для буфера ввода-вывода полностью уходит из-под контроля менеджера памяти Python.
См. также
Переменная окружения PYTHONMALLOC может использоваться для настройки распределителей памяти, используемых Python.
Переменная окружения PYTHONMALLOCSTATS может использоваться для вывода статистики распределителя памяти pymalloc каждый раз при создании новой арены объектов pymalloc, а также при завершении работы.
Сырой интерфейс памяти¶Raw Memory Interface
Следующие наборы функций являются обёртками вокруг системного аллокатора. Эти функции потокобезопасны, удерживать 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, никаких действий не выполняется.
Интерфейс памяти¶Memory Interface
Следующие наборы функций, основанные на стандарте 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)
Аллокаторы объектов¶Object allocators
Следующие наборы функций, основанные на стандарте 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, никаких действий не выполняется.
Настройка распределителей памяти¶Customize Memory Allocators
Новое в версии 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.
Нововыделенная память заполняется байтом
0xCB, освобождённая память заполняется байтом0xDB.Проверки во время выполнения:
Обнаруживает нарушения 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.
Распределитель pymalloc¶The pymalloc allocator
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()в противном случае.
Настройка аллокатора арен pymalloc¶Customize pymalloc Arena Allocator
Новое в версии 3.4.
-
PyObjectArenaAllocator¶ Структура, используемая для описания аллокатора арен. Структура содержит три поля:
Поле
Значение
void *ctxконтекст пользователя, передаваемый первым аргументом
void* alloc(void *ctx, size_t size)выделить арену размером bytes
void free(void *ctx, size_t size, void *ptr)освободить арену
-
PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)¶ Получить аллокатор арен.
-
PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)¶ Установить аллокатор арен.
Примеры¶Examples
Вот пример из раздела Обзор, переписанный так, чтобы буфер ввода-вывода выделялся из кучи Python с помощью первого набора функций:
PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* для ввода-вывода */
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); /* для ввода-вывода */
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.