В обработчике после создания малого memoryview память большого исходного буфера не освобождается: какой механизм это объясняет?
memoryview обычно не копирует данные при создании среза, а сохраняет ссылку на исходный объект-буфер. Поэтому даже маленькое представление может удерживать в памяти весь большой буфер, пока живы оно или производные от него представления.
Это экономит время и пиковую память при чтении данных без копирования, но может привести к неожиданному удержанию крупных объектов. Если нужен независимый небольшой фрагмент, следует явно создать копию.
Буферный протокол появился для обмена бинарными данными между объектами без промежуточного копирования. Он полезен для массивов байтов, файловых буферов и числовых структур, где копирование больших блоков заметно влияет на производительность.
memoryview предоставляет унифицированный доступ к такому буферу. Исходная задача — разделить данные между компонентами с минимальными затратами, сохранив контроль над форматом, размером и представлением элементов.
Предположим, приложение загружает крупный пакет, а затем сохраняет только небольшой заголовок как memoryview. Логически приложению нужен небольшой фрагмент, но физически этот фрагмент может продолжать ссылаться на весь исходный пакет.
Неверная интерпретация приводит к росту потребления памяти: удаление исходной переменной не помогает, потому что буфер всё ещё достижим через представление. Особенно опасен такой эффект в очередях, кэшах и долгоживущих объектах, где маленькие представления живут значительно дольше исходных сообщений.
Срез memoryview обычно является новым представлением диапазона существующего буфера, а не новым независимым массивом байтов. Представление хранит ссылку на экспортирующий объект или на цепочку представлений, поэтому исходный буфер не может быть уничтожен, пока доступ к его данным потенциально возможен.
В примере header_view занимает небольшой объём как объект представления, но удерживает большой bytearray. Преобразование в bytes создаёт отдельную копию 32 байт; после освобождения представления исходный буфер больше не удерживается этим фрагментом.
Главный компромисс таков: представление минимизирует копирование и обычно снижает задержки, но продлевает время жизни экспортирующего буфера. Копия, напротив, требует времени и памяти при создании, зато позволяет освободить большой источник независимо от маленького результата.
memoryview.release() прекращает использование буфера данным представлением, но не уничтожает сам объект, если на него существуют другие ссылки. Простое удаление исходной переменной также недостаточно, если живы представление, его срезы или другой владелец буфера.
Следует учитывать и совместимость с экспортирующим объектом: некоторые операции могут быть запрещены, пока активен memoryview, например изменение размера изменяемого буфера. Для диагностики нужно искать цепочку ссылок и время жизни представлений, а не только измерять размер самого объекта memoryview.
Сервис разбирал многомегабайтные сетевые сообщения и сохранял 20-байтовые идентификаторы в очереди. Вариант с memoryview почти устранил копирование и ускорил обработку, но очередь удерживала целые сообщения до завершения обработки. В результате память росла пропорционально числу ожидающих идентификаторов.
Рассматривались два решения. Хранить представления было быстро и не требовало копий, но связывало срок жизни идентификатора со сроком жизни сообщения. Немедленно преобразовывать идентификаторы в bytes требовало небольшого копирования, зато разрывало эту связь.
Выбрали копирование только короткого идентификатора в момент помещения в очередь, а memoryview оставили для синхронных операций внутри разбора. Это немного увеличило CPU-затраты, но позволило освобождать большие сообщения сразу после обработки и стабилизировало потребление памяти.
Нет, если само представление всё ещё существует. Удаление одной ссылки уменьшает счётчик ссылок на буфер, но ссылка, удерживаемая memoryview, остаётся. Нужно завершить жизнь всех представлений либо явно вызвать release() там, где это уместно.
Нет. Он дешевле по памяти и времени создания, если данные можно безопасно использовать совместно и срок жизни буфера контролируем. Если маленький фрагмент должен пережить большой источник, представление может удерживать гораздо больше памяти, чем копия, поэтому копирование становится более эффективным решением на всём жизненном цикле данных.
Размер объекта представления описывает служебную структуру представления, а не объём экспортирующего объекта. Сам буфер находится отдельно и может быть значительно больше диапазона, доступного через представление. При анализе нужно учитывать граф ссылок и владельца буфера, а не только результат поверхностной оценки размера представления.