Spec-Zone.ru › Python 3.13

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

Обзор

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

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

См. также

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

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

Домены распределения памяти

Все функции выделения памяти относятся к одному из трёх различных «доменов» (см. также PyMemAllocatorDomain). Эти домены представляют разные стратегии выделения памяти и оптимизированы для разных целей. Конкретные сведения о том, как каждый домен выделяет память и какие внутренние функции вызывает, считаются деталями реализации; однако для отладки упрощённую таблицу можно найти здесь. API, используемые для выделения и освобождения блока памяти, должны принадлежать одному и тому же домену. Например, для освобождения памяти, выделенной с помощью PyMem_Malloc(), необходимо использовать PyMem_Free().

Существуют три домена выделения памяти:

  • Домен Raw: предназначен для выделения памяти под буферы общего назначения, когда выделение должно выполняться системным распределителем или когда распределитель может работать без GIL. Память запрашивается непосредственно у системы. См. Интерфейс необработанной памяти.
  • Домен «Mem»: предназначен для выделения памяти под буферы Python и буферы общего назначения, когда выделение должно выполняться при удерживаемом GIL. Память берётся из частной кучи Python. См. Интерфейс памяти.
  • Домен объектов: предназначен для выделения памяти под объекты Python. Память берётся из частной кучи Python. См. Распределители объектов.

Примечание

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

Например, буферы (не являющиеся объектами Python) следует выделять с помощью PyMem_Malloc(), PyMem_RawMalloc() или malloc(), но не с помощью PyObject_Malloc().

См. API выделения памяти.

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

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

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

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

void *PyMem_RawMalloc(size_t n)
Входит в стабильный ABI начиная с версии 3.13.

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

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

void *PyMem_RawCalloc(size_t nelem, size_t elsize)
Входит в стабильный ABI начиная с версии 3.13.

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

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

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

void *PyMem_RawRealloc(void *p, size_t n)
Входит в стабильный ABI начиная с версии 3.13.

Изменяет размер блока памяти, на который указывает 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)
Входит в стабильный ABI начиная с версии 3.13.

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

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

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

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

Распределитель памяти по умолчанию использует распределитель памяти pymalloc.

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

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

Изменено в версии 3.6: теперь по умолчанию используется pymalloc вместо системного malloc().

void *PyMem_Malloc(size_t n)
Входит в стабильный ABI.

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

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

void *PyMem_Calloc(size_t nelem, size_t elsize)
Входит в стабильный ABI начиная с версии 3.7.

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

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

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

void *PyMem_Realloc(void *p, size_t n)
Входит в стабильный ABI.

Изменяет размер блока памяти, на который указывает 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)
Входит в стабильный ABI.

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

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

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

PyMem_New(TYPE, n)

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

PyMem_Resize(p, TYPE, 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)

Распределители объектов

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

Примечание

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

Распределитель объектов по умолчанию использует распределитель памяти pymalloc.

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

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

void *PyObject_Malloc(size_t n)
Входит в стабильный ABI.

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

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

void *PyObject_Calloc(size_t nelem, size_t elsize)
Входит в стабильный ABI начиная с версии 3.7.

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

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

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

void *PyObject_Realloc(void *p, size_t n)
Входит в стабильный ABI.

Изменяет размер блока памяти, на который указывает 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)
Входит в стабильный ABI.

Освобождает блок памяти, на который указывает 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.
  • mimalloc: распределитель памяти mimalloc. Если поддержка mimalloc недоступна, будет использоваться распределитель pymalloc.
  • «+ отладка»: включены отладочные перехватчики распределителей памяти Python.
  • «Отладочная сборка»: сборка Python в режиме отладки.

Настройка распределителей памяти

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

type 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.

type 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 не удерживается.

Для остальных областей распределитель также должен быть потокобезопасным: его могут вызывать разные интерпретаторы, которые не используют общую GIL.

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

См. также PyPreConfig.allocator и Предварительная инициализация Python с помощью PyPreConfig.

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

Функция PyMem_SetAllocator() имеет следующие ограничения:

  • Её можно вызвать после Py_PreInitialize() и до Py_InitializeFromConfig(), чтобы установить пользовательский распределитель памяти. Для установленного распределителя нет ограничений, кроме тех, что накладываются областью (например, в Raw Domain распределитель может вызываться без удержания GIL). Дополнительные сведения см. в разделе об областях распределителей.
  • Если вызвать её после завершения инициализации Python (после вызова Py_InitializeFromConfig()), распределитель должен оборачивать существующий распределитель. Подмена текущего распределителя каким-либо произвольным не поддерживается.

Изменено в версии 3.12: Все распределители должны быть потокобезопасными.

void PyMem_SetupDebugHooks(void)

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

Отладочные перехватчики в распределителях памяти Python

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

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

Функцию PyMem_SetupDebugHooks() можно использовать для установки отладочных перехватчиков после вызова PyMem_SetAllocator().

Эти отладочные перехватчики заполняют динамически выделенные блоки памяти специальными распознаваемыми битовыми шаблонами. Нововыделенная память заполняется байтом 0xCD (PYMEM_CLEANBYTE), освобождённая память — байтом 0xDD (PYMEM_DEADBYTE). Блоки памяти окружены «запрещёнными байтами», заполненными байтом 0xFD (PYMEM_FORBIDDENBYTE). Последовательности таких байтов вряд ли будут корректными адресами, числами с плавающей точкой или строками ASCII.

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

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

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

Пусть S = sizeof(size_t). С каждой стороны каждого запрошенного блока размером N байт добавляется 2*S байт. Расположение памяти выглядит следующим образом, где p обозначает адрес, возвращённый функцией типа malloc или realloc (p[i:j] означает срез байтов от *(p+i) включительно до *(p+j) исключительно; обратите внимание, что обработка отрицательных индексов отличается от среза Python):

p[-2*S:-S]

Изначально запрошенное количество байтов. Это значение типа size_t в порядке от старшего байта к младшему (его проще читать в дампе памяти).

p[-S]

Идентификатор API (символ ASCII):

  • 'r' для PYMEM_DOMAIN_RAW.
  • 'm' для PYMEM_DOMAIN_MEM.
  • 'o' для PYMEM_DOMAIN_OBJ.
p[-S+1:0]

Копии PYMEM_FORBIDDENBYTE. Используются для обнаружения записи и чтения перед началом блока.

p[0:N]

Запрошенная память, заполненная копиями PYMEM_CLEANBYTE; используется для обнаружения обращений к неинициализированной памяти. При вызове функции типа realloc с запросом большего блока памяти новые дополнительные байты также заполняются PYMEM_CLEANBYTE. При вызове функции типа free они перезаписываются значением PYMEM_DEADBYTE, чтобы обнаруживать обращения к освобождённой памяти. При вызове функции типа realloc с запросом меньшего блока памяти лишние старые байты также заполняются PYMEM_DEADBYTE.

p[N:N+S]

Копии PYMEM_FORBIDDENBYTE. Используются для обнаружения записи и чтения за концом блока.

p[N+S:N+2*S]

Используется только в том случае, если определён макрос PYMEM_DEBUG_SERIALNO (по умолчанию он не определён).

Порядковый номер, увеличиваемый на 1 при каждом вызове функции типа malloc или realloc. size_t в порядке от старшего байта к младшему. Если позднее обнаруживается «повреждённая память», порядковый номер позволяет легко установить точку останова при следующем запуске и зафиксировать момент, когда этот блок был передан вызывающему коду. Статическая функция bumpserialno() в obmalloc.c — единственное место, где увеличивается порядковый номер; она существует, чтобы упростить установку такой точки останова.

Функция типа realloc или free сначала проверяет, что байты PYMEM_FORBIDDENBYTE с обеих сторон блока не изменены. Если они изменены, диагностическое сообщение выводится в stderr, а программа аварийно завершается с помощью Py_FatalError(). Другой основной тип сбоя — ошибка памяти, возникающая, когда программа считывает один из специальных битовых шаблонов и пытается использовать его как адрес. Если в этот момент вы подключитесь к отладчику и посмотрите на объект, то, вероятно, увидите, что он полностью заполнен PYMEM_DEADBYTE (то есть используется освобождённая память) или PYMEM_CLEANBYTE (то есть используется неинициализированная память).

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

Изменено в версии 3.8: Байтовые шаблоны 0xCB (PYMEM_CLEANBYTE), 0xDB (PYMEM_DEADBYTE) и 0xFB (PYMEM_FORBIDDENBYTE) заменены на 0xCD, 0xDD и 0xFD, чтобы использовать те же значения, что и отладочные malloc() и free() Windows CRT.

Распределитель pymalloc

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

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

Распределитель арен использует следующие функции:

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

Этот распределитель отключён, если Python настроен с параметром --without-pymalloc. Его также можно отключить во время выполнения с помощью переменной окружения PYTHONMALLOC (например, PYTHONMALLOC=malloc).

Обычно имеет смысл отключить распределитель pymalloc при сборке Python с AddressSanitizer (--with-address-sanitizer), который помогает выявлять низкоуровневые ошибки в коде C.

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

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

type PyObjectArenaAllocator

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

Поле

Значение

void *ctx

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

void* alloc(void *ctx, size_t size)

выделить арену размером size байт

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

освободить арену

void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)

Получить распределитель арен.

void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)

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

Распределитель mimalloc

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

Python поддерживает распределитель mimalloc, если это позволяет базовая платформа. mimalloc «представляет собой распределитель общего назначения с отличными характеристиками производительности. Изначально он был разработан Дааном Лейеном для систем выполнения языков Koka и Lean».

C API tracemalloc

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

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

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

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

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

int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)

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

Если tracemalloc отключён, возвращает -2, в противном случае — 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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/c-api/memory.html

Spec-Zone.ru

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