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

Обзор

Управление памятью в Python включает в себя приватную кучу, содержащую все объекты и структуры данных Python. Управление этой приватной кучей осуществляется с помощью Python memory manager. Диспетчер памяти 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); /* 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 memory allocator каждый раз, когда создается новый объект pymalloc arena, а также при завершении работы.

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

Все распределяющие функции принадлежат к одному из трех разных «доменов» (см. также : c:type:PyMemAllocatorDomain). Эти домены представляют разные стратегии распределения и оптимизированы для разных целей. Конкретные сведения о том, как каждый домен выделяет память или какие внутренние функции вызывает каждый домен, рассматриваются как детали реализации, но для целей отладки упрощенную таблицу можно найти по адресу here. Нет жесткого требования использовать память, возвращаемую функциями выделения, принадлежащими данному домену, только для целей, указанных в этом домене (хотя это рекомендуемая практика). Например, можно было бы использовать память, возвращаемую PyMem_RawMalloc() для выделения объектов Python, или память, возвращаемую PyObject_Malloc() для выделения памяти для буферов.

Тремя областями распределения являются:

  • Необработанный домен: предназначен для выделения памяти для буферов памяти общего назначения, где выделение должно передаваться системному распределителю или где распределитель может работать без GIL. Память запрашивается непосредственно у системы.

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

  • Объектный домен: предназначен для выделения памяти, принадлежащей объектам Python. Память берется из частной кучи Python.

При освобождении памяти, ранее выделенной выделяющими функциями, принадлежащими к данному домену, необходимо использовать соответствующие специальные функции освобождения. Например, для освобождения памяти, выделенной с помощью PyMem_Free(), необходимо использовать PyMem_Malloc().

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

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

В default raw memory allocator используются следующие функции: 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, который должен был быть возвращен предыдущим вызовом:c:func:PyMem_RawMalloc, PyMem_RawRealloc() или PyMem_RawCalloc(). В противном случае, или если PyMem_RawFree(p) был вызван ранее, возникает неопределенное поведение.

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

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

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

В default memory allocator используется pymalloc memory allocator.

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

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

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

void *PyMem_Malloc(size_t n)
Part of the Стабильный ABI.

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

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

void *PyMem_Calloc(size_t nelem, size_t elsize)
Part of the Стабильный ABI since version 3.7.

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

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

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

void *PyMem_Realloc(void *p, size_t n)
Part of the Стабильный 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)
Part of the Стабильный ABI.

Освобождает блок памяти, на который указывает p, который должен был быть возвращен предыдущим вызовом:c:func: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 при перехвате выделяющих функций в этой области методами, описанными в разделе Customize Memory Allocators.

В default object allocator используется pymalloc memory allocator.

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

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

void *PyObject_Malloc(size_t n)
Part of the Стабильный ABI.

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

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

void *PyObject_Calloc(size_t nelem, size_t elsize)
Part of the Стабильный ABI since version 3.7.

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

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

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

void *PyObject_Realloc(void *p, size_t n)
Part of the Стабильный 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)
Part of the Стабильный ABI.

Освобождает блок памяти, на который указывает p, который должен был быть возвращен предыдущим вызовом:c:func: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 + отладка

Легенда:

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

Добавлено в версии 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_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(), чтобы переустановить отладочные перехватчики сверху на новом распределителе.

Смотрите также : c:member:PyPreConfig.allocator и Preinitialize Python with PyPreConfig.

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

PyMem_SetAllocator() действительно имеет следующий контракт:

  • Он может быть вызван после Py_PreInitialize() и перед Py_InitializeFromConfig() для установки пользовательского распределителя памяти. Для установленного распределителя нет никаких ограничений, кроме тех, которые налагаются доменом (например, необработанный домен позволяет вызывать распределитель без сохранения GIL). Дополнительную информацию смотрите в разделе the section on allocator domains.

  • При вызове после завершения инициализации Python (после вызова Py_InitializeFromConfig()) распределитель ** должен ** обернуть существующий распределитель. Замена текущего распределителя на какой-либо другой произвольный распределитель ** не поддерживается**.

void PyMem_SetupDebugHooks(void)

Установите debug hooks in the Python memory allocators для обнаружения ошибок в памяти.

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

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

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

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

Эти отладочные перехватчики заполняют динамически выделяемые блоки памяти специальными, узнаваемыми битовыми шаблонами. Вновь выделенная память заполняется байтом 0xCD (PYMEM_CLEANBYTE), освобожденная память заполняется байтом 0xDD (PYMEM_DEADBYTE). Блоки памяти окружены «запрещенными байтами», заполненными символом byte 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). 2*S байты добавляются в конце каждого запрошенного блока из N байт. Схема расположения памяти выглядит следующим образом, где p представляет адрес, возвращаемый функцией, подобной malloc, или функцией, подобной realloc (p[i:j] означает фрагмент байтов от *(p+i) включительно до *(p+j) исключительных; обратите внимание, что обработка отрицательных индексов отличается от среза Python):

p[-2*S:-S]

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

p[-S]

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

p[-S+1:0]

Копии PYMEM_FORBIDDENBYTE. Используется для перехвата ошибок при записи и чтении.

p[0:N]

Запрошенная память, заполненная копиями байта PYMEM_CLEAN, используется для перехвата ссылки на неинициализированную память. Когда вызывается функция, подобная realloc, запрашивающая больший блок памяти, новые лишние байты также заполняются PYMEM_CLEANBYTE. Когда вызывается функция, подобная свободной памяти, они перезаписываются с помощью 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. Если «плохая память» будет обнаружена позже, серийный номер дает отличный способ установить точку останова при следующем запуске, чтобы зафиксировать момент, когда этот блок был отключен. Статическая функция dump serial no() в obmalloc.c - это единственное место, где увеличивается серийный номер, и она существует, чтобы вы могли легко установить такую точку останова.

Функция, подобная realloc или free-like, сначала проверяет, что байты PYMEM_FORBIDDENBYTE на каждом конце не повреждены. Если они были изменены, диагностический вывод записывается в stderr, и программа прерывается из-за ошибки Py_Fatal(). Другой основной способ сбоя - это ошибка памяти, когда программа считывает один из специальных битовых шаблонов и пытается использовать его в качестве адреса. Если вы тогда зайдете в отладчик и посмотрите на объект, вы, вероятно, увидите, что он полностью заполнен 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, чтобы использовать те же значения, что и в Windows CRT debug malloc() и free().

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

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

pymalloc - это default allocator из доменов PYMEM_DOMAIN_MEM (например: PyMem_Malloc()) и PYMEM_DOMAIN_OBJ (например: PyObject_Malloc()).

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

  • VirtualAlloc() и VirtualFree() в Windows,

  • mmap() и munmap(), если доступно,

  • malloc() и free() в противном случае.

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

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

Добавлено в версии 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)

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

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); /* 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.

Вернуться на верх