В Android 17 и более поздних версиях включен Memory Limiter — системная служба, которая отслеживает и ограничивает использование памяти процессами приложений с помощью Linux cgroup v2. Memory Limiter предотвращает непропорциональное потребление системной памяти отдельными приложениями, что снижает общую нагрузку на память и предотвращает агрессивное завершение критически важных процессов переднего плана и кэшированных процессов при нехватке памяти.
Механизм
Ограничитель памяти интегрируется со службой управления активностью (AMS) для отслеживания событий жизненного цикла процесса и изменений состояния. Ограничитель памяти обеспечивает соблюдение ограничений памяти, используя иерархию cgroup v2 ядра Linux.
Для поддержки ограничителя памяти ядро устройства должно включить 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 .
Конфигурация
Ограничитель памяти считывает свою конфигурацию из раздела vendor :
- Путь к файлу:
/vendor/etc/memory-limiter-config.xml
формат XML
Конфигурационный файл соответствует схеме, определенной в memory-limiter-config.xsd . В файле определены несколько наборов ограничений, упорядоченных по доступной памяти; служба выбирает наиболее подходящий набор ограничений на основе доступной оперативной памяти устройства. Все значения памяти указываются в мебибайтах (МиБ).
<MemoryLimiterConfig>
<version>1</version>
<configList>
<!-- RAM minimums account for MemTotal excluding carve-outs. -->
<limitSet>
<!-- Limits for 16GB RAM device (MemTotal >= 15GiB): 10G/5G/5G/5G -->
<minimumRequiredMemTotal>15360</minimumRequiredMemTotal>
<memVisible>10240</memVisible>
<memNotVisible>5120</memNotVisible>
<swapVisible>5120</swapVisible>
<swapNotVisible>5120</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 12GB RAM device (MemTotal >= 11GiB): 8G/4G/4G/4G -->
<minimumRequiredMemTotal>11264</minimumRequiredMemTotal>
<memVisible>8192</memVisible>
<memNotVisible>4096</memNotVisible>
<swapVisible>4096</swapVisible>
<swapNotVisible>4096</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 8GB RAM device (MemTotal >= 7GiB): 5G/3G/3G/3G -->
<minimumRequiredMemTotal>7168</minimumRequiredMemTotal>
<memVisible>5120</memVisible>
<memNotVisible>3072</memNotVisible>
<swapVisible>3072</swapVisible>
<swapNotVisible>3072</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 6GB RAM device (MemTotal >= 5GiB): 4G/2G/2G/2G -->
<minimumRequiredMemTotal>5120</minimumRequiredMemTotal>
<memVisible>4096</memVisible>
<memNotVisible>2048</memNotVisible>
<swapVisible>2048</swapVisible>
<swapNotVisible>2048</swapNotVisible>
</limitSet>
<limitSet>
<!-- Limits for 4GB RAM device (MemTotal >= 3GiB): 2G/1G/1G/1G -->
<minimumRequiredMemTotal>3072</minimumRequiredMemTotal>
<memVisible>2048</memVisible>
<memNotVisible>1024</memNotVisible>
<swapVisible>1024</swapVisible>
<swapNotVisible>1024</swapNotVisible>
</limitSet>
</configList>
</MemoryLimiterConfig>
-
version - Положительное целое число, указывающее на версию конфигурации. Оно должно быть
1. -
minimumRequiredMemTotal Минимальный
MemTotalсистемы (в МиБ), необходимый для применения этого ограничения. Служба сравнивает это значение с общим объемом доступной памяти, сообщаемым ядром в файле/proc/meminfo.Понимание выделения памяти: значение
MemTotalядра отражает объем физической оперативной памяти, доступной операционной системе после вычета выделенной памяти для аппаратного и встроенного обеспечения. Выделенная память резервируется для специализированных аппаратных компонентов, таких как графический процессор, модем базовой полосы, процессор обработки изображений камеры, защищенные среды выполнения и гипервизоры, обычно занимая от 500 МиБ до более 1 ГиБ физической памяти.Поскольку
MemTotalменьше заявленного физического объема DRAM устройства, пороговые значенияminimumRequiredMemTotalвключают запас для этих ограничений. Например, для конфигурации, предназначенной для устройств с 16 ГБ памяти, значениеminimumRequiredMemTotalсоставляет15360(15 ГиБ), а не16384(16 ГиБ), а для устройств с 12 ГБ памяти —11264(11 ГиБ). Это гарантирует, что каждое устройство будет соответствовать своему предполагаемому уровню независимо от различий в ограничениях на уровне платы.-
memVisible Мягкое ограничение памяти (
memory.high) в МиБ применяется к видимым процессам.-
memNotVisible Мягкое ограничение на использование памяти (
memory.high) в МиБ применяется к невидимым процессам.-
swapVisible Ограничение объема подкачки (
memory.swap.max) в МиБ применяется к видимым процессам.-
swapNotVisible Ограничение на объем подкачки (
memory.swap.max) в МиБ применяется к невидимым процессам.
Стандартные ограничения платформы
В Android 17 и более поздних версиях платформа задает стандартные ограничения на использование памяти и файла подкачки в файле /vendor/etc/memory-limiter-config.xml для всех распространенных уровней физической оперативной памяти:
| Физическая оперативная память | Минимально необходимые MemTotal (minimumRequiredMemTotal) | Ограничение видимой памяти (memVisible) | Невидимый лимит памяти (memNotVisible) | Видимый лимит свопа (swapVisible) | Не отображается лимит свопа (swapNotVisible) |
|---|---|---|---|---|---|
| 16 Гб | 15 360 МиБ (15 ГиБ) | 10 240 МиБ (10 ГиБ) | 5120 МиБ (5 ГиБ) | 5120 МиБ (5 ГиБ) | 5120 МиБ (5 ГиБ) |
| 12 ГБ | 11 264 МиБ (11 ГиБ) | 8192 МиБ (8 ГиБ) | 4096 МиБ (4 ГиБ) | 4096 МиБ (4 ГиБ) | 4096 МиБ (4 ГиБ) |
| 8 ГБ | 7168 МиБ (7 ГиБ) | 5120 МиБ (5 ГиБ) | 3072 МиБ (3 ГиБ) | 3072 МиБ (3 ГиБ) | 3072 МиБ (3 ГиБ) |
| 6 ГБ | 5120 МиБ (5 ГиБ) | 4096 МиБ (4 ГиБ) | 2048 МиБ (2 ГиБ) | 2048 МиБ (2 ГиБ) | 2048 МиБ (2 ГиБ) |
| 4ГБ | 3072 МиБ (3 ГиБ) | 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 для обозначения MiB или суффикс GB для обозначения GiB:
# 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
Ручные настройки применяются только в течение времени жизни конкретного экземпляра процесса. Если процесс перезапускается, он возвращается к стандартным ограничениям, основанным на его состоянии.
При ручном управлении невозможно превысить физические ограничения системы.