Почему одно и то же приложение стабильно работает на одном смартфоне и вылетает на другом

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

Источник изображения: предоставлено автором материала

19 июня 2026 года сооснователь Nothing Акис Евангелидис объяснил решение не выпускать смартфон CMF Phone 3 Pro в этом году. Построить модель, которая выглядела бы шагом вперёд по сравнению с предыдущей и сохранила бы прежнюю цену, не получилось из-за подорожавших чипов памяти. Это первый случай, когда производитель прямо называет рост цен на память причиной отмены модели, а не переноса сроков. За решением стоит более широкий сдвиг: рост цен на чипы памяти уже давит на весь рынок и особенно на бюджетный сегмент, где производители вынуждены урезать конфигурации оперативной памяти. На практике это означает конкретный сценарий: пиковое потребление памяти приложением укладывается в бюджет старшего устройства, но превышает объём, который операционная система может предоставить ему на модели с меньшим количеством RAM. Раньше инженер узнавал об этом несоответствии только после сбоя в проде. Максим Мартынов, ведущий программист Playrix с опытом в разработке более 10 лет, эксперт-судья AITEX Summit Spring 2026 и жюри Cases & Faces, предложил измеримый способ такой оценки на этапе проектирования. С Максимом поговорили о том, при каких условиях аллокатор держит память впустую, а при каких укладывается в физический бюджет устройства.

Что упускают стандартные тесты

Стандартные тесты аллокаторов, такие как Larson и Google Threadtest, измеряют пропускную способность — количество операций выделения и освобождения памяти, которое система выполняет в секунду. Однако они не показывают, какой объём памяти аллокатор удерживает сверх реально используемого. Мартынов столкнулся с этой проблемой на практике: в системах, где одновременно работают миллионы объектов, важно понимать, какой объём памяти каждая архитектура резервирует сверх необходимого. Готового инструмента для такой оценки у него не было, поэтому он выделил отдельный показатель — отношение зарезервированного объёма памяти к пиковому объёму живых данных, которое назвал коэффициентом накладных расходов. В его исследовании этот показатель используется наряду с пропускной способностью и масштабируемостью нагрузки для оценки поведения разных архитектур аллокаторов. При объёме доступной памяти 4–6 ГБ величина такого резерва может становиться существенным архитектурным ограничением.

«Лабораторный прогон и работа приложения на реальном устройстве проверяют разные вещи. Синтетический тест занимает считанные минуты и не показывает, как аллокатор ведёт себя, когда одни объекты живут секунды, а другие часы, и всё это происходит в одной памяти одновременно. Поэтому я вынес модель времени жизни объекта в отдельную переменную эксперимента, а не оставил её побочным эффектом», – поясняет Мартынов.

Иными словами, скорость аллокатора не равна его аппетиту к памяти: хороший результат по одному показателю не гарантирует хорошего результата по другому.

Формализовав коэффициент накладных расходов, Мартынов перешёл к главному вопросу: как ведут себя реальные архитектуры аллокаторов под разными сценариями нагрузки. В работе «Object Lifetime Models and Their Effect on Performance and Memory Fragmentation in Specialized C++ Allocators» он сравнил четыре аллокатора: системный malloc, monotonic arena (аллокатор на основе монотонно растущего пула памяти), pool allocator (пул-аллокатор для объектов одного размера) и segregated free list (аллокатор со списками свободных блоков, разделёнными по классам размеров). Он построил матрицу из 39 контролируемых сценариев на пересечении модели времени жизни объектов и характера нагрузки. Long-lived означает, что объекты живут долго и освобождаются массово, FIFO, LIFO и random описывают индивидуальное освобождение в разном порядке. Характер нагрузки задаёт, с чем работает аллокатор: фиксированный малый размер, переменный размер или интенсивная ротация, когда объекты постоянно создаются и уничтожаются. Отдельно Мартынов проверил каждый аллокатор при десятикратном росте нагрузки: именно на этом пределе архитектурные допущения либо продолжают работать, либо перестают.

Что показали цифры

По исследованию Максима Мартынова, в рамках эксперимента системный malloc показал наиболее стабильную, хотя и сравнительно невысокую пропускную способность: изменение модели времени жизни и характера нагрузки влияло на него меньше, чем на специализированные реализации – во всех 39 сценариях malloc держится в диапазоне 0,87–1,07 миллиона операций в секунду. Там, где нагрузка соответствует допущениям более специализированной архитектуры, malloc проигрывает ей на один-два порядка.

В том же исследовании pool allocator на объектах с длительным временем жизни и фиксированным размером достигает 86,4 миллиона операций в секунду при коэффициенте накладных расходов 1,000×: весь зарезервированный объём занят полезными данными без остатка. Мартынов зафиксировал, что при десятикратном росте нагрузки показатель почти не падает – 81,4 миллиона операций в секунду. Это означает, что pool allocator не теряет эффективность при резком росте числа одновременно живых объектов на сцене или интенсивности создания временных объектов, как бывает в сложной игровой сцене. На индивидуальном освобождении объектов (FIFO, LIFO, random) тот же аллокатор показывает 46,0–49,0 миллиона операций в секунду, ниже пикового значения, но по-прежнему на порядок выше malloc.

Третий аллокатор, monotonic arena, держит 50,3–59,5 миллиона операций в секунду при фиксированном коэффициенте накладных расходов 1,25×. В исследованной конфигурации monotonic arena применялась в сценариях, где объекты освобождаются группой: отдельные освобождения она не обрабатывает, а занятая память возвращается только при сбросе всей арены. Небольшой, но постоянный резерв возникает из-за политики роста блоков, когда текущий блок заполняется и аллокатор выделяет новый, оставляя часть последнего неиспользованной.

Самый неожиданный результат Мартынов получил на segregated free list. При интенсивной ротации объектов, когда живых данных всего 8729 байт, аллокатор держит в резерве больше 2 миллионов байт: коэффициент накладных расходов достигает 236,5×. При переменном размере объектов накладные расходы держатся на уровне 7,6–8,0× при пропускной способности 31,2–35,5 миллиона операций в секунду. В сценариях с долгоживущими объектами коэффициент, напротив, падает ниже 1× и доходит до 0,08×, но это не признак эффективности: суммарный объём живых объектов превышает ёмкость заранее выделенных размерных классов, и аллокатор переходит в аварийный режим отказа, а не экономит память. При десятикратном росте нагрузки система переходит в аварийный режим: 2 002 194 события исчерпания ёмкости размерных классов. Как отмечает специалист, причина в том, что в исследованной реализации сегрегированный список свободных блоков заранее и агрессивно преаллоцирует размерные классы: при подходящей нагрузке оно даёт скорость, при несовпадении тратит память впустую или приводит к отказу обслуживания.

«Перед выбором аллокатора команде нужно зафиксировать распределение размеров, диапазон времени жизни объектов, порядок освобождения и максимальную интенсивность ротации. Эти четыре параметра позволяют заранее оценить резервирование памяти и риск исчерпания размерных классов», – комментирует Максим Мартынов.

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

Что это значит для отрасли

Кризис цен на память не меняет саму математику работы аллокаторов: она была такой же и год, и три года назад. Меняется то, какие её последствия становятся заметны. Пока на устройстве было 12–16 гигабайт памяти, разрыв между зарезервированным и реально используемым объёмом оставался деталью, которую можно было не замечать. При 4–6 гигабайтах он превращается в ограничение: приложение, технически работающее при полном объёме памяти, может не поместиться на урезанном устройстве, потому что аллокатор держит часть памяти пустой про запас, и этот резерв не вписывается в сокращённый бюджет. Выбор архитектуры аллокатора перестаёт быть деталью оптимизации и становится частью спецификации на объём памяти, доступный продукту. На практике это означает, что инженер может сверить архитектуру аллокатора с бюджетом памяти устройства ещё на этапе проектирования, а не после того, как пользователи бюджетных смартфонов начнут жаловаться на зависания и вылеты приложения. Для отрасли, где рост цен на компоненты уже меняет продуктовые линейки, методика Мартынова даёт воспроизводимый способ проверить архитектуру аллокатора заранее, а не постфактум, когда приложение перестаёт помещаться в бюджет памяти устройства.