В Android 17 и более поздних версиях есть Ограничитель памяти – системный сервис, который отслеживает и ограничивает использование памяти процессами приложений с помощью Linux cgroup v2. Ограничитель памяти не позволяет отдельным приложениям потреблять непропорционально большой объем системной памяти, что снижает общую нагрузку на память и предотвращает агрессивное завершение работы критически важных процессов переднего плана и кэшированных процессов из-за нехватки памяти.
Механизм
Ограничитель памяти интегрирован с сервисом Activity Manager Service (AMS), чтобы отслеживать события жизненного цикла процессов и изменения состояния. Ограничитель памяти обеспечивает соблюдение лимитов памяти с помощью иерархии cgroup v2 ядра Linux.
Чтобы поддерживать Memory Limiter, ядро устройства должно включать cgroup v2 и контроллер memory. Сервис использует следующие атрибуты:
memory.high- Регулируемое ограничение. Если лимит превышен, процесс замедляется и ядро пытается освободить страницы, используемые этим процессом.
memory.swap.max- Ограничивает максимальный объем пространства подкачки (например, ZRAM), который может использовать процесс.
Влияние на приложения
Ограничитель памяти не влияет на приложения, которые не превышают лимиты.
Когда приложение превышает лимит memory.high, ядро вытесняет из памяти, поддерживаемой файлами, чистые данные приложения и переносит неактивные анонимные данные в файл подкачки, чтобы приложение не выходило за рамки лимита. В результате вытеснения страниц и обмена данными приложение может временно работать медленнее, но оно продолжает выполняться.
В крайних случаях, например при неконтролируемой утечке памяти, когда приложение продолжает выделять анонимную память после исчерпания пространства подкачки, приложению не удается выделить память, и оно завершает работу.
Мониторинг процессов
По умолчанию ограничитель памяти отслеживает процессы приложений (UID >= 10000). Исключение составляют основные системные процессы, необходимые для стабильной работы устройства.
Ограничитель памяти назначает лимиты на основе состояния видимости процесса:
Видимые процессы. Процессы, в которых размещен видимый пользователю интерфейс (например, текущее действие или активное взаимодействие). Поскольку для отображения пользовательского интерфейса требуется больший рабочий набор для конвейеров рендеринга и графики, видимые процессы получают более щедрый лимит памяти.
Невидимые процессы. Процессы, которые выполняются в фоновом режиме без отображения пользовательского интерфейса (например, фоновые службы или приемники широковещательных намерений). Поскольку они не отрисовывают компоненты интерфейса, для них действуют более строгие ограничения.
В таблице ниже показано, как состояния процессов соотносятся с классификацией ограничений памяти.
| Состояние процесса | Лимит памяти |
|---|---|
PERSISTENT | Без ограничений |
PERSISTENT_UI | Без ограничений |
TOP | Видимость |
BOUND_TOP | Видимость |
FOREGROUND_SERVICE | Не отображается |
BOUND_FOREGROUND_SERVICE | Не отображается |
IMPORTANT_FOREGROUND | Видимость |
IMPORTANT_BACKGROUND | Не отображается |
TRANSIENT_BACKGROUND | Не отображается |
BACKUP | Не отображается |
SERVICE | Не отображается |
RECEIVER | Не отображается |
TOP_SLEEPING | Видимость |
HEAVY_WEIGHT | Не отображается |
HOME | Не отображается |
LAST_ACTIVITY | Не отображается |
CACHED_ACTIVITY | Кешировано |
CACHED_ACTIVITY_CLIENT | Кешировано |
CACHED_RECENT | Кешировано |
CACHED_EMPTY | Кешировано |
В кешированном состоянии процессы замораживаются, а затем максимально освобождаются.
Если процесс превышает заданный лимит memory.high, ограничитель памяти обнаруживает событие и может запустить диагностические действия, например создать профилировщик памяти или зарегистрировать отклонение в statsd.
Конфигурация
В Android 17 и более поздних версиях ограничитель памяти настраивается с помощью стандартного файла конфигурации платформы в системном разделе по пути /system/etc/memory-limiter-config.xml.
Формат XML
Файл конфигурации соответствует схеме, определенной в memory-limiter-config.xsd. В файле определено несколько наборов ограничений, упорядоченных по объему доступной памяти. Сервис выбирает набор с наибольшим объемом, который соответствует доступной оперативной памяти устройства. Все значения памяти указаны в мебибайтах (МиБ).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM boundaries account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device [14336, 18432): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>14336</minimumRequiredMemTotal>
<maximumAllowedMemTotal>18432</maximumAllowedMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device [9216, 14336): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>9216</minimumRequiredMemTotal>
<maximumAllowedMemTotal>14336</maximumAllowedMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device [6800, 9216): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>6800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>9216</maximumAllowedMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device [4800, 6800): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>4800</minimumRequiredMemTotal>
<maximumAllowedMemTotal>6800</maximumAllowedMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device [3200, 4800): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3200</minimumRequiredMemTotal>
<maximumAllowedMemTotal>4800</maximumAllowedMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
version- Положительное целое число, обозначающее версию конфигурации. Это
1. minimumRequiredMemTotalМинимальный объем системной
MemTotal(в МиБ, включая нижнюю границу), необходимый для применения этого набора ограничений. Сервис сравнивает это значение с общим объемом доступной памяти, который сообщает ядро в/proc/meminfo.Понимание выделения памяти и границ. Значение ядра
MemTotalотражает физическую память DRAM, доступную операционной системе после вычета памяти, выделенной для аппаратного и встроенного ПО. Выделенные области резервируют ОЗУ для специализированных аппаратных компонентов, таких как графический процессор, модем базовой полосы частот, ISP камеры, безопасные среды выполнения и гипервизоры. Обычно они занимают от 500 МБ до 2 ГБ физической памяти.Поскольку
MemTotalменьше, чем заявленный объем физической памяти DRAM, пороговые значенияminimumRequiredMemTotalвключают запас для этих вычетов. Например, для конфигурации, предназначенной для устройств с 16 ГБ памяти, указывается минимальное значение14336(14 ГиБ), а не16384(16 ГиБ), а для устройств с 12 ГБ памяти –9216(9 ГиБ). Это гарантирует, что каждое устройство будет соответствовать своему уровню независимо от вариаций на уровне платы.maximumAllowedMemTotalМаксимальный объем системной памяти
MemTotal(в МиБ, исключая верхний предел), при котором применяется этот набор ограничений. Если значениеMemTotalбольше или равно этому потолку, установленное ограничение не применяется. Для устройств с объемом памяти 18 432 МиБ и более или менее 3200 МиБ ограничитель памяти не применяется, и процессы не ограничиваются.memVisibleМягкое ограничение памяти (
memory.high) в МиБ, применяемое к видимым процессам.memNotVisibleМягкое ограничение памяти (
memory.high) в МиБ, применяемое к невидимым процессам.swapVisibleОграничение на размер раздела подкачки (
memory.swap.max) в МБ, применяемое к видимым процессам.swapNotVisibleОграничение на размер раздела подкачки (
memory.swap.max) в МиБ, применяемое к невидимым процессам.
Стандартные ограничения платформы
В Android 17 и более поздних версиях платформа задает стандартные лимиты памяти и файла подкачки в файле /system/etc/memory-limiter-config.xml для распространенных уровней физической ОЗУ, используя интервалы диапазона [floor, ceiling):
| Целевая физическая ОЗУ |
Диапазон MemTotal [нижний предел, верхний предел) (minimumRequiredMemTotal, maximumAllowedMemTotal) |
Видимый лимит памяти (memVisible) |
Лимит памяти для невидимых объявлений (memNotVisible) |
Ограничение видимых замен (swapVisible) |
Невидимый лимит обмена (swapNotVisible) |
|---|---|---|---|---|---|
| 16 ГБ | [14336, 18432) МиБ |
10 240 МиБ (10 ГиБ) | 5120 МиБ (5 ГиБ) | 5120 МиБ (5 ГиБ) | 5120 МиБ (5 ГиБ) |
| 12 ГБ | [9216, 14336) МиБ |
8192 МиБ (8 ГиБ) | 4096 МиБ (4 ГиБ) | 4096 МиБ (4 ГиБ) | 4096 МиБ (4 ГиБ) |
| 8 ГБ | [6800, 9216) МиБ |
5120 МиБ (5 ГиБ) | 3072 МиБ (3 ГиБ) | 3072 МиБ (3 ГиБ) | 3072 МиБ (3 ГиБ) |
| 6 ГБ | [4800, 6800) МиБ |
4096 МиБ (4 ГиБ) | 2048 МиБ (2 ГиБ) | 2048 МиБ (2 ГиБ) | 2048 МиБ (2 ГиБ) |
| 4 ГБ | [3200, 4800) МиБ |
2048 МиБ (2 ГиБ) | 1024 МиБ (1 ГиБ) | 1024 МиБ (1 ГиБ) | 1024 МиБ (1 ГиБ) |
Принципы ограничения памяти
Конфигурация ограничителя памяти основана на следующих принципах:
Согласованность экосистемы и совместимость приложений. Единые ограничения памяти на разных устройствах обеспечивают предсказуемую производительность приложений в экосистеме Android. Приложения разрабатываются и тестируются с учетом стандартных требований к памяти. Единые ограничения платформы позволяют избежать неожиданного снижения производительности или преждевременного завершения работы.
Пропорциональное распределение ресурсов. Лимиты платформы калибруются в соответствии с емкостью физической ОЗУ:
- Видимые процессы. Выделено примерно от 1/2 до 2/3 от общего объема физической ОЗУ для поддержки активного пользовательского интерфейса, композиции и рабочих нагрузок отрисовки.
- Невидимые процессы. Для фоновых задач выделяется примерно от 1/4 до 1/3 от общего объема физической ОЗУ.
Универсальность. Ограничения применяются ко всем процессам приложений на устройстве (UID >= 10000), включая предустановленные системные приложения и приложения от OEM-производителей. Ограничитель памяти не поддерживает белые списки для исключения определенных приложений, обеспечивая справедливое управление памятью для всех приложений.
Отсутствие API для запросов во время выполнения. В Android 17 и более поздних версиях приложения не могут программно запрашивать назначенные им ограничения памяти во время выполнения. Стандартные ограничения платформы установлены с большим запасом, чтобы приложения, работающие корректно, не испытывали ограничений при обычном использовании.
Динамика возврата памяти ядром и обмена. Когда процесс приложения приближается к лимиту
memory.high, ядро Linux направляет возврат памяти именно в cgroup этого процесса. Это включает удаление неактивных страниц, поддерживаемых файлами, и перенос холодных анонимных страниц в ZRAM, что позволяет поддерживать плавную работу системы без создания глобальной нагрузки на память.
Команды оболочки
Команда am memory-limiter позволяет разработчикам и системным интеграторам взаимодействовать с сервисом во время выполнения для разработки, тестирования и отладки:
am memory-limiter <SUB-COMMAND>статус
Подчиненная команда status позволяет получить отчет об оперативном статусе и активных показателях ограничителя памяти:
adb shell am memory-limiter statusПример выходных данных:
Memory limiter
enabled monitoring=true ignored=none
visibleMem=1948MB visibleSwap=974MB
notVisibleMem=974MB notVisibleSwap=487MB
started=36 watched=36 watch-failed=0
events=0 processes=36 process-hwm=36
Основные поля в выходных данных:
monitoring- Указывает, отслеживает ли Ограничитель памяти процессы.
visibleMemиnotVisibleMem- Рассчитанные абсолютные ограничения памяти (
memory.high), действующие в настоящее время для каждого состояния видимости. visibleSwapиnotVisibleSwap- Рассчитанные абсолютные лимиты на обмен (
memory.swap.max), которые в настоящее время применяются для каждого состояния видимости. events- Количество случаев, когда процесс превысил назначенный лимит.
processes- Текущее количество отслеживаемых процессов.
игнорировать
Подчиненная команда ignore временно исключает определенный UID или все процессы из ограничения памяти. Это полезно для оценки производительности, стресс-тестирования или диагностики поведения памяти:
# Ignore a specific UID adb shell am memory-limiter ignore 10087# Ignore all processes (temporarily disables limiting) adb shell am memory-limiter ignore all# Resume normal limiting operation adb shell am memory-limiter ignore none
вручную
Субкоманда manual переопределяет рассчитанные ограничения для определенного процесса по идентификатору процесса (PID) с помощью настраиваемого абсолютного значения в байтах. Значение должно быть целым числом, но может включать суффикс MB для указания МиБ или суффикс GB для указания ГиБ:
# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1073741824# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1024MB# Set a 1GiB limit for PID 1234 adb shell am memory-limiter manual 1234 1GB# Remove the manual override for PID 1234 adb shell am memory-limiter manual 1234 none
Переопределения, заданные вручную, действуют только в течение времени выполнения определенного экземпляра процесса. Если процесс перезапустится, будут восстановлены стандартные ограничения в зависимости от состояния ресурса.
Заданное вручную значение не может превышать физические ограничения системы.