Для эффективной оптимизации использования памяти вашей игрой необходимо сначала понять, как платформа Android измеряет объем памяти и как использовать системную телеметрию, диагностические API и инструменты профилирования. В этом руководстве подробно описано, как отслеживать, фиксировать и анализировать выделение памяти вашей игрой в соответствии с новыми рекомендациями платформы.
Понимание метрик RSS и свопов
Для эффективного анализа и отладки поведения памяти в вашей игре необходимо понимать точные технические метрики, используемые платформой Android для управления памятью. Подробную информацию о том, как этот параметр телеметрии обрабатывается и отслеживается в реальных условиях, см. в документации Android Vitals - Memory usage (anonymous RSS + swap) .
1. Анонимный RSS (RssAnon)
Размер резидентного набора (RSS) измеряет долю памяти, занимаемую процессом и хранящуюся в физической оперативной памяти устройства. RSS делится на память, поддерживаемую файлами, и анонимную память. Метрика памяти Android фокусируется исключительно на анонимном RSS:
- Что это включает : страницы памяти, выделенные непосредственно игровым процессом и не связанные с физическим файлом на хранилище. К таким страницам относятся кучи Java или Kotlin, стеки выполнения потоков и, что особенно важно, собственные выделения памяти (например, пользовательские распределители памяти движка C++ или блоки памяти, запрошенные с помощью нативных функций malloc или new и измененные игровой логикой). Подробнее об этом показателе можно узнать в словаре Process Memory (RSS) .
- Почему это важно : Игровые движки используют огромные пулы собственной памяти для обработки физики, рендеринга и логики. Поскольку эти пулы не поддерживаются файлами, они полностью хранятся в анонимных RSS-лентах и составляют основную часть физического объема памяти вашей игры.
2. Несжатый файл подкачки (VmSwap)
Android не поддерживает традиционное дисковое пространство подкачки из-за износа флэш-памяти и ограничений по задержке. Вместо этого он использует zRAM (несжатое пространство подкачки):
- Что это включает : Когда увеличивается нагрузка на физическую оперативную память, демон управления памятью ядра сжимает неактивные анонимные страницы и перемещает их в выделенную несжатую часть физической оперативной памяти (zRAM).
- Расчет метрики : Система отслеживает это на основе несжатого размера (VmSwap), чтобы оценить фактическую потребность игры в физической памяти. Если ваша игра выделяет память, а система выгружает ее в zRAM, это все равно учитывается в общем объеме используемой памяти вашей игры.
3. Состояния процесса
В Android Vitals данные об использовании памяти разбиты по состояниям процессов . Для разработчиков игр сторонние SDK или игры также могут неожиданно запускать службы, воспринимаемые пользователем, или фоновые службы.
- Что включает в себя : передний план, воспринимаемые сервисы, фоновый режим и кэш.
- Почему это важно : Различные состояния процессов по-разному влияют на управление памятью в ОС Android. Вы можете не знать, что ваша игра работает в чувствительном состоянии процесса, если какой-либо сторонний SDK непреднамеренно запускает фоновую задачу. Отслеживайте, работает ли ваша игра в фоновом режиме, с помощью
RunningAppProcessInfo.
Интерфейсы прикладного программирования (API)
Android предоставляет системные API, которые позволяют вашей игре динамически реагировать на нехватку памяти и собирать подробную диагностику памяти во время выполнения.
Реагировать на события очистки памяти
Система использует onTrimMemory для уведомления вашего приложения о событиях жизненного цикла, которые предоставляют хорошую возможность для вашего приложения добровольно сократить использование памяти и избежать завершения работы с помощью механизма LMK (low-memory killer) для освобождения памяти для других приложений.
Если система завершает работу вашего приложения в фоновом режиме, пользователь столкнется с медленным «холодным» запуском при возобновлении работы. Сокращение использования памяти в фоновом режиме помогает предотвратить такие завершения работы.
При реагировании на события обрезки освобождайте большие, восстанавливаемые объемы памяти, которые не требуются немедленно:
Пример: Удаление или очистка кэшированных растровых изображений (декодированных из локального хранилища) в ответ на
TRIM_MEMORY_UI_HIDDEN.
Котлин
class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
override fun onTrimMemory(level: Int) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
Java
public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
public void onTrimMemory(int level) {
switch (level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
}
ProfilingManager
Представленный в Android 15 (уровень API 35), API ProfilingManager позволяет приложениям программно создавать снимки состояния (такие как профили кучи, трассировки системы и дампы кучи Java) непосредственно во время выполнения.
Разработчики могут вручную запускать захват данных в определенных сценах или регистрировать автоматические триггеры, такие как TRIGGER_TYPE_ANOMALY для автоматического запуска захвата данных, когда игровой процесс превышает пороговые значения лимита памяти. Однако разработчикам игр необходимо учитывать критические ограничения современных игровых движков:
Примечание: Современные игровые движки (такие как Unity или Unreal) управляют производительностью выполнения, предварительно выделяя большие блоки виртуальной памяти из ядра с помощью mmap с флагом MAP_ANONYMOUS. Затем движки используют собственные суб-аллокаторы (например, собственный менеджер памяти Unity или BinnedAllocators Unreal) для внутреннего разделения и выделения блоков памяти.
ApplicationExitInfo
Если ваша игра завершается в фоновом режиме или принудительно завершается из-за превышения лимитов памяти отдельного процесса, стандартные механизмы Java или собственные механизмы создания дампов аварийного завершения (например, Firebase Crashlytics) не регистрируют это событие. Для программного запроса и регистрации таких завершений разработчикам следует использовать API ApplicationExitInfo при запуске игры.
- Реализация: При запуске вызовите
ActivityManager.getHistoricalProcessExitReasons(), чтобы получить причины завершения последних сессий. - Основные причины выхода из памяти:
-
REASON_LOW_MEMORY: Указывает на то, что процесс был завершен системным механизмом Low Memory Killer (LMK). Это завершение происходит, когда нагрузка на память устройства высока, и ОС должна освободить оперативную память. Эта причина завершения указывает на то, что фоновый объем памяти, занимаемый вашей игрой, слишком велик для сосуществования с другими приложениями. -
REASON_MEMORY_LIMITER(Android 17 (уровень API 37) и выше): указывает на то, что процесс был завершен из-за превышения лимита памяти cgroup (RssAnon + VmSwap), установленного ограничителем памяти платформы. Такое завершение может произойти даже при наличии достаточного объема физической памяти на устройстве, что свидетельствует о прямом нарушении лимитов отдельных процессов.
-
Используйте доступные инструменты
Используйте следующие инструменты платформы во время разработки и контроля качества, чтобы точно измерить использование памяти вашей игры.
meminfo
Этот инструмент собирает статистику использования памяти, чтобы показать, сколько памяти PSS было выделено и для каких категорий она использовалась.
Вывести статистику meminfo можно одним из следующих способов:
- Используйте команду
adb shell dumpsys meminfo package-name. - Используйте вызов
MemoryInfoиз API отладки Android.
Статистика PrivateDirty показывает объем оперативной памяти внутри процесса, который не может быть выгружен на диск и не используется другими процессами. Большая часть этого объема становится доступной системе после завершения этого процесса.
Точки трассировки памяти
Точки трассировки памяти отслеживают объем памяти RSS , используемой вашей игрой. Расчет использования памяти RSS намного быстрее, чем расчет использования памяти PSS. Благодаря более быстрому расчету, RSS показывает более детальные изменения размера памяти, что позволяет более точно измерять пиковое использование памяти. Поэтому легче заметить пики, которые могут привести к нехватке памяти в игре.
Перфетто
Perfetto — это набор инструментов для сбора информации о производительности и памяти устройства и отображения её в веб-интерфейсе. Он поддерживает трассировки произвольной длины, позволяя отслеживать изменение RSS с течением времени. Вы также можете выполнять SQL-запросы к полученным данным для автономной обработки. Включите длинные трассировки в приложении «Системная трассировка» . Убедитесь, что для трассировки включена категория memory:Memory . Для пользовательской инструментации памяти в разработке и тестировании вы также можете использовать API heapprofd (бета-версия).
Проверьте RSS-Anon и замените его на Perfetto.
Чтобы проверить влияние анонимной памяти и файла подкачки zRAM на вашу игру, загрузите файл трассировки в веб-интерфейсе по адресу ui.perfetto.dev и следуйте этим аналитическим методам, разработанным для глубокого анализа памяти (подробнее см. раздел «Примеры анализа памяти в Perfetto» ):
1. Визуализация счетчиков памяти на временной шкале
- Найдите свой процесс: в списке навигации найдите название пакета или процесса вашей игры.
- Развернуть группу дорожек: Щелкните строку процесса, чтобы развернуть дорожки потоков, и найдите подгруппу с именем «Память».
- Проанализируйте следы:
- mem.rss.anon (Анонимный RSS) : Этот линейный график показывает объем физической оперативной памяти, занимаемой неуправляемыми пулами памяти вашей игры в режиме реального времени. Отслеживайте эту временную шкалу во время загрузки сцен, всплывающих окон пользовательского интерфейса или переходов в игровом процессе, чтобы проверить наличие пиков выделения памяти.
- mem.swap (Сжатый файл подкачки или VmSwap) : Этот график отображает размер предварительно сжатых блоков памяти, перемещенных в zRAM. Высокая активность файла подкачки, совпадающая с игровым процессом, указывает на то, что ваша игра работает на устройстве с ограниченной памятью, и система активно сжимает фоновые ресурсы.
2. Выполнение SQL-запросов (обработчик трассировки) Для детального анализа в автономном режиме вы можете выполнять SQL-запросы непосредственно в консоли пользовательского интерфейса Perfetto или использовать автономную библиотеку Python для обработки трассировки, чтобы вычислить статистические пики.
Найдите пиковое значение распределения анонимных RSS-каналов:
SELECT max(value) / 1024 / 1024 AS max_rss_anon_mb FROM counter JOIN counter_track ON counter.track_id = counter_track.id WHERE counter_track.name = 'mem.rss.anon' AND counter_track.upid IN ( SELECT upid FROM process WHERE name = 'your.game.package.name' );Сопоставьте значения RssAnon и VmSwap в любой заданный момент времени:
SELECT ts, track.name AS metric_type, value / 1024 / 1024 AS size_mb FROM counter JOIN counter_track track ON counter.track_id = track.id WHERE (track.name = 'mem.rss.anon' OR track.name = 'mem.swap') AND track.upid IN ( SELECT upid FROM process WHERE name = 'your.game.package.name' ) ORDER BY ts ASC;
Для получения более подробной информации о проверке файлов трассировки с помощью Android Studio см. раздел «Проверка системных трассировок: память процесса (RSS)» . Для получения подробной информации о создании сценариев для профилей памяти см. раздел «Запись выделенных собственных ресурсов» .
heapprofd
heapprofd — это инструмент для отслеживания памяти, входящий в состав Perfetto. Этот инструмент помогает находить утечки памяти, показывая, где была выделена память с помощью malloc . heapprofd можно запустить с помощью скрипта Python, и, поскольку инструмент имеет низкие накладные расходы, он не влияет на производительность, в отличие от других инструментов, таких как Malloc Debug.
отчет об ошибке
bugreport — это инструмент для ведения логов, позволяющий выяснить, произошла ли ошибка в игре из-за нехватки памяти. Вывод инструмента гораздо подробнее, чем у logcat . Он полезен для отладки проблем с памятью, поскольку показывает, произошла ли ошибка в игре из-за нехватки памяти или же она была завершена процессом LMK.
Для получения дополнительной информации см. раздел «Сбор и чтение отчетов об ошибках» .
инструменты игрового движка
Хотя журналы на уровне платформы и системная телеметрия имеют решающее значение для отслеживания пороговых значений ОС и соответствия требованиям, инструменты, специфичные для игровых движков, помогают напрямую соотносить выделение ресурсов с игровыми объектами, поведением скриптов и активной иерархией сцен.
Единство
В среде Unity Engine вы можете с высокой степенью надежности оценить объем памяти, занимаемой Android Anonymous RSS + Swap, во время выполнения (обычно отклонение составляет менее 10% по сравнению с реальными значениями на уровне ОС), используя встроенные инструменты и классы профилирования Unity.
Полное пошаговое руководство, включая правила настройки и скрипты для выполнения, см. в разделе «Как проверить память с помощью инструментов Unity» .
- API профилировщика Unity : Вы можете программно приблизительно оценить объем неуправляемой памяти вашей игры во время выполнения, запросив основные метрики движка:
- Использование класса Profiler : отслеживайте общий объем выделенной памяти, суммируя значения методов
Profiler.GetTotalReservedMemoryLong()иProfiler.GetMonoHeapSizeLong(). - Использование класса
ProfilerRecorder: динамически отслеживайте категории памяти. Для получения надежного базового значения, получите значение Total Reserved Memory (в релизных сборках) или вычтите из него значение Gfx Reserved Memory (в сборках для разработчиков), чтобы исключить компоненты графической памяти, поддерживаемые файлами.
- Использование класса Profiler : отслеживайте общий объем выделенной памяти, суммируя значения методов
- Профилировщик памяти Unity : Чтобы выявить и отладить утечки памяти в автономном режиме, сделайте снимок памяти и изучите диаграмму «Резидентная память на устройстве», расположенную в разделе «Вся память». Для расчета приблизительного объема занимаемой памяти суммируйте значения следующих категорий: «Неотслеживаемая», «Среда выполнения Android», «Нативная» и «Управляемая».
- Ограничение zRAM : В условиях ограниченного объема памяти ядро Android может сжимать неактивные страницы памяти в пространство подкачки (zRAM). Поскольку Unity Memory Profiler не может определять параметры подкачки на уровне ОС, вы можете заметить незначительные расхождения в объеме занимаемой памяти во время ресурсоемких сценариев. Сравните свои оценки с Perfetto, чтобы подтвердить точные значения.