Spec-Zone.ru › Python 3.14

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

Обзор

Управление памятью в 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: предназначен для выделения памяти под буферы общего назначения, для которых память должна выделяться системным распределителем или распределитель может работать без присоединенного состояния потока. Память запрашивается непосредственно у системы. См. Интерфейс необработанной памяти.
  • Домен «Mem»: предназначен для выделения памяти под буферы Python и буферы общего назначения, для которых память должна выделяться при наличии присоединенного состояния потока. Память берется из закрытой кучи Python. См. Интерфейс памяти.
  • Домен Object: предназначен для выделения памяти под объекты Python. Память берется из закрытой кучи Python. См. Распределители объектов.

Примечание

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

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

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

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

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

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

В сборке с GIL (сборка по умолчанию) распределитель памяти по умолчанию использует распределитель памяти pymalloc, тогда как в сборке без глобальной блокировки интерпретатора вместо него по умолчанию используется распределитель памяти mimalloc.

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

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

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

Изменено в версии 3.13: В сборке без глобальной блокировки интерпретатора теперь по умолчанию используется mimalloc.

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().

Устаревшие псевдонимы

Это мягко устаревшие псевдонимы существующих функций и макросов. Они существуют исключительно для обратной совместимости.

Устаревший псевдоним

Соответствующая функция или макрос

PyMem_MALLOC(size)

PyMem_Malloc()

PyMem_NEW(type, size)

PyMem_New

PyMem_REALLOC(ptr, size)

PyMem_Realloc()

PyMem_RESIZE(ptr, type, size)

PyMem_Resize

PyMem_FREE(ptr)

PyMem_Free()

PyMem_DEL(ptr)

PyMem_Free()

Изменено в версии 3.4: Теперь макросы являются псевдонимами соответствующих функций и макросов. Ранее их поведение было таким же, но их использование не обязательно сохраняло двоичную совместимость между версиями Python.

Устарело начиная с версии 2.0.

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

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

Примечание

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

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

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

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

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, операция не выполняется.

Не вызывайте эту функцию напрямую для освобождения памяти объекта; вместо этого вызывайте слот tp_free типа.

Не используйте эту функцию для освобождения памяти, выделенной с помощью PyObject_GC_New или PyObject_GC_NewVar; вместо этого используйте PyObject_GC_Del().

См. также

  • PyObject_GC_Del() является эквивалентом этой функции для памяти, выделенной типами, поддерживающими сборку мусора.
  • PyObject_Malloc()
  • PyObject_Realloc()
  • PyObject_Calloc()
  • PyObject_New
  • PyObject_NewVar
  • PyType_GenericAlloc()
  • tp_free

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

Стандартные распределители памяти:

Конфигурация

Имя

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 + отладка

Сборка без GIL

"mimalloc"

mimalloc

mimalloc

mimalloc

Отладочная сборка без GIL

"mimalloc_debug"

mimalloc + отладка

mimalloc + отладка

mimalloc + отладка

Условные обозначения:

  • Имя: значение переменной среды PYTHONMALLOC.
  • malloc: системные распределители из стандартной библиотеки C, функции C: malloc(), calloc(), realloc() и free().
  • pymalloc: распределитель памяти pymalloc.
  • mimalloc: распределитель памяти mimalloc.
  • «+ отладка»: с отладочными перехватчиками распределителей памяти 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.

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

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

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

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

  • Её можно вызвать после Py_PreInitialize() и до Py_InitializeFromConfig(), чтобы установить пользовательский распределитель памяти. Установленный распределитель должен соблюдать только ограничения, накладываемые областью (например, в области Raw распределитель может вызываться без присоединённого состояния потока). Дополнительные сведения см. в разделе об областях распределителей.
  • Если функция вызывается после завершения инициализации 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().
  • Обнаружение записи перед началом буфера (выход за границы буфера в сторону начала).
  • Обнаружение записи после конца буфера (переполнение буфера).
  • Проверка наличия присоединённого состояния потока при вызове функций распределителя в областях 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, чтобы получить трассировку стека места выделения блока памяти. Отладочные перехватчики теперь также проверяют наличие присоединённого состояния потока при вызове функций в областях 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)

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

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.

В отличие от pymalloc, оптимизированного для небольших объектов (размером не более 512 байт), mimalloc обрабатывает выделение памяти любого размера.

В сборке без GIL mimalloc является стандартным и обязательным распределителем для областей PYMEM_DOMAIN_MEM и PYMEM_DOMAIN_OBJ. В сборках без GIL его нельзя отключить. В такой сборке используются кучи mimalloc для каждого потока, что позволяет в большинстве случаев выполнять выделение и освобождение памяти без блокировок.

В стандартной сборке (с GIL) mimalloc доступен, но не является распределителем по умолчанию. Его можно выбрать во время выполнения с помощью PYTHONMALLOC=mimalloc (или mimalloc_debug, чтобы включить отладочные перехватчики). Его можно отключить во время сборки с помощью параметра настройки --without-mimalloc, но этот параметр нельзя использовать вместе с --disable-gil.

API C модуля 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_Free(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_Free()  */

Помимо функций для работы с необработанными блоками памяти из кучи Python, объекты Python выделяются и освобождаются с помощью PyObject_New, PyObject_NewVar и PyObject_Free().

Эти функции будут рассмотрены в следующей главе, посвящённой определению и реализации новых типов объектов на C.

© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/c-api/memory.html

Spec-Zone.ru

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