Демон управления памятью

В Android 17 и более поздних версиях поддерживается демон управления памятью (mmd) – системный демон, который обрабатывает конфигурацию демона, настраиваемые параметры и текущие задачи по обслуживанию файла подкачки или ZRAM.

Фон

До появления mmd конфигурации ZRAM в Android были фрагментированы и предлагали ограниченные возможности настройки. mmd решает эту проблему, централизуя управление ZRAM, позволяя использовать более сложную логику конфигурации и упрощая добавление новых функций и улучшений архитектуры. mmd также обеспечивает четкое разделение задач между процессом на основе Java system_server и подкачкой или управлением памятью на уровне ядра.

Архитектура и управление ZRAM

После завершения загрузки (то есть когда sys.boot_completed=1) mmd_setup пытается настроить ZRAM с указанными параметрами. После завершения настройки ZRAM система включает сервис mmd, который выполняет текущие задачи по обслуживанию.

В проекте mmd операции обслуживания инициируются из system_server путем отправки запросов Binder в mmd с помощью интерфейса IMmd. mmd выполняет задачи по обслуживанию, такие как запись данных из ZRAM, повторное сжатие и запись данных для каждого процесса, на основе собственного внутреннего механизма правил. И расписание из ActivityManagerService, и правила обслуживания ZRAM можно настроить с помощью системных свойств.

Интеграция системного сервера (system_server)

Процесс system_server на основе Java определяет, когда вызывается mmd. Этот процесс позволяет отделить глобальную очистку от оптимизации памяти для отдельных приложений.

Обычное обслуживание после обработки

Глобальное обслуживание ZRAM выполняется сервисом ActivityManagerService с помощью com.android.server.memory.ZramMaintenance.

zram-maintenance

Рисунок 1. Схема планирования обслуживания ZRAM.

  • Планировщик. ZramMaintenance регистрирует периодическую фоновую задачу в JobScheduler Android.
  • Ограничения для заданий. Чтобы предотвратить заикание интерфейса и конфликты ЦП, для задания явно заданы значения setRequiresDeviceIdle(true) и setRequiresBatteryNotLow(true).
  • Триггер Binder. Когда планировщик запускает onStartJob(), system_server вызывает mmd.doZramMaintenanceAsync(). Это односторонний асинхронный вызов Binder. system_server не блокирует ожидание завершения очистки. mmd ставит эту задачу в очередь фонового потока, чтобы выполнить повторное сжатие и запись последовательно.

Обратная запись для каждого процесса

Удаление памяти на уровне отдельных процессов управляется сервисом ActivityManagerService с помощью функции com.android.server.am.CachedAppOptimizer.

mmd-writeback

Рисунок 2. Процесс обратной записи mmd.

Когда процесс переходит в фоновое кешированное состояние, ActivityManager выполняет сжатие памяти. Если завершение процесса из-за нехватки памяти будет заметно пользователю (то есть процесс размещает Activity) и если запись в ZRAM для каждого процесса приведет к тому, что объем памяти, занимаемый процессом, будет близок к нулю, система выполняет следующие действия:

  1. После сжатия CachedAppOptimizer отправляет отложенное сообщение (ZRAM_WRITEBACK_MSG) своему внутреннему обработчику сжатия (с задержкой на mZramWritebackWaitSeconds).
  2. Когда задержка закончится, ActivityManager откроет дескриптор файла безопасного процесса pidfd.
  3. Сервер системы вызывает mmd.asyncWritebackProcessZramMemory(pfd, callback).
  4. mmd выполняет ioctl для обратной записи на процесс и отправляет отчет с помощью IMmdProcessWritebackCallback. Если все пройдет успешно, ActivityManager пометит запись процесса (setIsZramWrittenBack(app, true)), чтобы повысить приоритет процесса oom_score_adj, и запишет показатели в FrameworkStatsLog.ZRAM_WRITEBACK_EVENT.

Упреждающий запрос для каждого процесса

Когда пользователь перезапускает ранее кешированное приложение (размороженное из-за UNFREEZE_REASON_ACTIVITY), ActivityManager минимизирует задержку запуска приложения, вызванную серьезными ошибками страниц из резервного хранилища:

  1. CachedAppOptimizer перехватывает событие разморозки и вызывает prefetchZram(app).
  2. Системный сервер отправляет pidfd приложения через Binder, используя mmd.asyncPrefetchProcessZramMemory(pfd). mmd выполняет ioctl ZRAM_ANDROID_IOC_PROCESS_PREFETCH, указывая ядру асинхронно предварительно загружать страницы подкачки обратно в ОЗУ, пока инициализируется основной поток пользовательского интерфейса приложения.

Обзор задач по обслуживанию и постобработке

В этом разделе описываются фоновые операции обслуживания и задачи постобработки, которые mmd выполняет для оптимизации swap-пространства и системной памяти.

Техническое обслуживание в mmd

В mmd обслуживание – это запланированные фоновые проверки, которые оптимизируют использование swap-пространства и физической памяти, не влияя на производительность активных приложений. Вместо того чтобы выполнять непрерывные синхронные проверки (которые приводили бы к частым пробуждениям ЦП и временным зависаниям интерфейса), обслуживание выполняется асинхронно:

  1. system_server периодически запускает doZramMaintenanceAsync() в Binder.

  2. mmd помещает запрос в очередь фоновых задач LowPrioWorkItem::ZramMaintenance.

  3. В mmd есть один рабочий поток, который управляет очередью с высоким и низким приоритетом. Задачи с высоким приоритетом (например, предварительная выборка для каждого процесса) обрабатываются первыми и могут вытеснять задачи с низким приоритетом. Обслуживание и запись данных на уровне процессов выполняются как задачи с низким приоритетом. После извлечения из очереди рабочий поток выполняет две основные операции обслуживания последовательно:

    • Повторное сжатие ZRAM. Проверяет существующие страницы подкачки и повторно сжимает неактивные страницы с помощью вторичного алгоритма с более высоким коэффициентом сжатия, например zstd.

    • ZRAM writeback. Сканирует неактивные страницы и полностью удаляет их из ОЗУ на резервное флеш-хранилище, представляющее собой петлевое устройство из файла на /data.

Задачи постобработки в ZRAM

В модуле ZRAM ядра Linux и архитектуре mmd задачи постобработки – это асинхронные преобразования, применяемые к страницам памяти после того, как они уже были выгружены стандартными путями восстановления ядра (kswapd или сжатие).

Когда страница впервые выгружается из памяти, система отдает приоритет скорости: она использует быстрый основной алгоритм сжатия (например, lz4) и сохраняет сжатую страницу в ОЗУ. Однако со временем многие страницы, перенесенные в файл подкачки, становятся холодными или неактивными, например приложения, которые были помещены в кеш в фоновом режиме и не запускались в течение нескольких часов. Оставлять неиспользуемые страницы в быстрой, слабо сжатой ZRAM неэффективно.

Конвейер постобработки

mmd использует многоэтапный жизненный цикл постобработки для оптимизации этих страниц:

mmd-page-lifecycle

Рисунок 3. mmd жизненный цикл страницы.

  1. Этап 1. Исходная замена (быстрое сжатие). Память освобождается с помощью kswapd или сжатия приложений. Обычно при первом восстановлении используется быстрый алгоритм сжатия, например lz4, а содержимое хранится в ОЗУ.

  2. Этап 2. Отметка неактивности (старение и отслеживание). Отслеживание неактивности обращается к отслеживанию памяти ядра (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) или использует свой программный маркер неактивности, чтобы отслеживать, как долго страницы остаются без изменений.mmd

  3. Этап 3. Постобработка 1 – повторное сжатие (освобождение памяти): страницы, достигшие возраста бездействия повторного сжатия (от min_idle_seconds до max_idle_seconds), подвергаются повторному сжатию. mmd записывает в /sys/block/zram0/recompress инструкцию для ядра о том, что страницу lz4 нужно распаковать и снова сжать с помощью zstd. Это позволяет освободить физическую память без износа флеш-памяти.

  4. Этап 4. Постобработка 2 – запись в память (выгрузка во флеш-память). Если нагрузка на память не снижается и страницы достигают возраста бездействия записи (обычно 20 часов или более), mmd запускает запись в память. mmd записывает в /sys/block/zram0/idle и /sys/block/zram0/writeback, чтобы полностью вытеснить сжатую страницу из ОЗУ в резервное флеш-хранилище.

Настройка ZRAM

mmd загружает и обрабатывает следующие свойства настройки ZRAM:

Свойство Использовать По умолчанию
mmd.zram.enabled Включена ли настройка ZRAM в mmd. false
mmd.zram.num_devices Количество устройств ZRAM, которые нужно настроить. Для числа N устройства zram0–zram<N-1> должны присутствовать, прежде чем система задаст значение sys.boot_completed=1. Свойства в списке устройств ZRAM можно настраивать для каждого устройства отдельно. 1
mmd.zram.device_priority Значения приоритета, которые нужно передавать при вызове swapon. Не задано
mmd.zram.comp_algorithm Алгоритм сжатия ZRAM. Если не указано иное, используется алгоритм сжатия ядра по умолчанию. Не задано
mmd.zram.size Размер устройства ZRAM в байтах или в процентах от размера ОЗУ устройства, например 75%. 50%
mmd.zram.writeback.enabled Включить ли запись в ZRAM. false
mmd.zram.writeback.device_size Размер устройства обратной записи в байтах или процентах от раздела данных. Фактический размер устройства может быть изменен в зависимости от доступного места в разделе данных. 1073741824 (1 ГиБ)
mmd.zram.writeback.min_free_space_mib Минимальный объем свободного места в МиБ, который должен быть доступен после настройки устройства с обратной записью. 1536 (1,5 ГиБ)
mmd.zram.writeback.use_nr_tags_prop Если задано значение true, то для настройки глубины очереди устройства петли, поддерживающего запись ZRAM, используется значение из mmd.zram.writeback.nr_tags. Это обходное решение для ситуаций, когда правила SELinux поставщика не могут быть настроены так, чтобы разрешить mmd напрямую считывать nr_tags блочного устройства, поддерживающего /data. false
mmd.zram.writeback.nr_tags Подробнее: mmd.zram.writeback.use_nr_tags_prop. Не задано
mmd.zram.recompression.enabled Включить или отключить функцию повторного сжатия ZRAM. false
mmd.zram.recompression.algorithm Дополнительный алгоритм повторного сжатия zRAM. zstd

Свойства устройств ZRAM

Если значение mmd.zram.num_devices больше единицы, можно настроить отдельные свойства для каждого устройства ZRAM, указав в качестве значения свойства разделенный запятыми список, содержащий ровно mmd.zram.num_devices элементов. К ним относятся:

  • mmd.zram.size
  • mmd.zram.comp_algorithm
  • mmd.zram.device_priority
  • mmd.zram.recompression.enabled
  • mmd.zram.recompression.huge_idle.enabled
  • mmd.zram.recompression.idle.enabled
  • mmd.zram.recompression.huge.enabled
  • mmd.zram.recompression.threshold_bytes
  • mmd.zram.recompression.algorithm
  • mmd.zram.writeback.device_size
  • mmd.zram.writeback.huge_idle.enabled
  • mmd.zram.writeback.idle.enabled
  • mmd.zram.writeback.huge.enabled

Прекращение поддержки существующей настройки ZRAM

Хотя swapon_all по-прежнему доступен в Android для настройки ZRAM и раздела подкачки на диске, mmd является предпочтительным подходом к управлению ZRAM, поскольку он обеспечивает более простую настройку и продвинутые функции, такие как повторное сжатие ZRAM.

Если mmd ZRAM включен mmd.zram.enabled:

  • Настройка ZRAM в реализации swapon_all становится недействительной.
  • Существующие конфигурации ZRAM, например config_zramWriteback в файле overlay config.xml и системные свойства обратной записи ro.zram.*, игнорируются.

Параметры настройки ZRAM

Обслуживание ZRAM должно работать без дополнительных настроек, но вы можете настроить его с помощью системных свойств, описанных в этом разделе.

Планирование обслуживания ZRAM

Эти свойства определяют, как и когда system_server планирует задачи по обслуживанию ZRAM.

Свойство Использовать По умолчанию
mm.zram.maintenance.first_delay_seconds Задержка перед началом первого обслуживания ZRAM. 3600 (1 час)
mm.zram.maintenance.periodic_delay_seconds Задержка между последующим планированием обслуживания ZRAM. 3600 (1 час)
mm.zram.maintenance.require_device_idle Укажите, следует ли запускать обслуживание ZRAM только в том случае, если устройство находится в режиме бездействия. true
mm.zram.maintenance.require_battery_not_low Требуется ли, чтобы перед запуском обслуживания ZRAM батарея была заряжена не менее чем на определенный процент. true

Правила обратной записи ZRAM

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

Свойство Использовать По умолчанию
mmd.zram.writeback.backoff_seconds Время ожидания с момента последней операции обратной записи. 600 (10 минут)
mmd.zram.writeback.min_idle_seconds В сочетании с mmd.zram.writeback.max_idle_seconds используется для расчета возраста простоя страницы, при котором она может быть записана на диск на основе доли использования памяти. Рассчитанный возраст бездействия экспоненциально интерполируется между двумя параметрами, чтобы минимизировать нагрузку, когда память не перегружена. 72000 (20 часов)
mmd.zram.writeback.max_idle_seconds Максимальное количество секунд, используемое для динамического расчета возраста неактивной страницы на основе использования памяти. 90000 (25 часов)
mmd.zram.writeback.huge.enabled Включить ли обратную запись страниц HUGE. false
mmd.zram.writeback.idle.enabled Включить ли обратную запись страницы IDLE. true
mmd.zram.writeback.huge_idle.enabled Включить ли обратную запись страницы HUGE_IDLE. true
mmd.zram.writeback.min_bytes Минимальное количество байтов, которые будут записаны за один цикл записи в режиме ожидания. 5242880 (5 МиБ)
mmd.zram.writeback.max_bytes Максимальное количество байтов, записываемых за один цикл обратной записи в режиме ожидания. 314572800 (300 МиБ)
mmd.zram.writeback.max_bytes_per_day Максимальное количество байтов, которые можно записать за 24 часа. 25769803776 (24 ГиБ)
mmd.zram.writeback.limit.enabled Включить ли учет дневного лимита бюджета для обратной записи. true

Правила повторного сжатия ZRAM

Следующие параметры определяют, когда и какой тип памяти сжимается повторно:

Свойство Использовать По умолчанию
mmd.zram.recompression.backoff_seconds Время ожидания с момента последнего повторного сжатия. 1800 (30 минут)
mmd.zram.recompression.min_idle_seconds В сочетании с mmd.zram.recompression.max_idle_seconds используется для расчета времени бездействия страницы, чтобы определить, можно ли повторно сжать ее на основе доли использования памяти. Рассчитанный возраст бездействия экспоненциально интерполируется между двумя параметрами, чтобы минимизировать нагрузку, когда память не перегружена. 7200 (2 часа)
mmd.zram.recompression.max_idle_seconds Максимальное количество секунд, используемое для динамического расчета возраста неактивной страницы. 14400 (4 часа)
mmd.zram.recompression.threshold_bytes Минимальный размер в байтах страниц ZRAM, которые можно сжать повторно. 1024 (1 КиБ)
mmd.zram.recompression.huge.enabled Включить или отключить повторное сжатие страницы HUGE. true
mmd.zram.recompression.idle.enabled Включить или отключить повторное сжатие страницы IDLE. true
mmd.zram.recompression.huge_idle.enabled Включить или отключить повторное сжатие страниц HUGE_IDLE. true

Отслеживание неактивных страниц ZRAM

mmd ZRAM maintenance отмечает страницы ZRAM как неактивные на основе того, сколько времени прошло с момента последнего доступа к ним. Для работы этой функции необходимо включить конфигурации ядра CONFIG_ZRAM_TRACK_ENTRY_ACTIME или CONFIG_ZRAM_MEMORY_TRACKING. CONFIG_ZRAM_TRACK_ENTRY_ACTIME включен по умолчанию в ядрах GKI 6.18 и более поздних версий. В более ранних версиях ядра она не включена по умолчанию и требует дополнительных ресурсов памяти.

Если конфигурация ядра не включена, mmd обслуживание ZRAM переходит на программный заменитель для отслеживания неактивных страниц ZRAM:

  1. Отметить все страницы ZRAM как неактивные при запуске mmd.

  2. Пропускать следующие проверки ZRAM, пока не пройдет необходимый период отсрочки.

  3. Запись в zRAM или повторное сжатие неактивных страниц. Если из-за ограничений на запись остаются неактивные страницы, mmd продолжает записывать страницы при следующем техническом обслуживании, не отмечая новые страницы как неактивные (пропуская шаг 4).

  4. Если все неактивные страницы были записаны, снова пометьте все страницы ZRAM как неактивные и вернитесь к шагу 2. Если запись в ZRAM отключена, mmd помечает все страницы ZRAM как неактивные, когда после периода неактивности происходит повторное сжатие ZRAM.

Устранение неполадок и проверка

Чтобы проверить и диагностировать работу mmd и ZRAM, выполните следующие действия по проверке и устранению неполадок.

Как проверить настройки ZRAM

Чтобы проверить, успешно ли mmd настроил ZRAM во время загрузки:

  1. Проверьте активный алгоритм сжатия и размер диска:

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. Проверьте системные свойства mmd и статус запущенного сервиса:

    getprop | grep mmd.zram
    dumpsys -l | grep mmd
    

Как проверить обслуживание и запись ZRAM

Убедитесь, что задачи обратной записи и повторного сжатия ZRAM работают:

  1. Проверьте статус базового блочного устройства:

    cat /sys/block/zram0/bd_stat
    
  2. Проверьте эффективность повторного сжатия, отслеживая /sys/block/zram0/mm_stat. Изменения в размерах сжатых данных должны появляться после циклов обслуживания.

Как проверить обратную запись для каждого процесса

Чтобы проверить, работает ли обратная запись для каждого процесса, можно использовать следующие команды:

  • Проверьте adb logcat -s mmd, чтобы найти журналы успешной обратной записи или диагностики ошибок.

Распространенные проблемы и диагностика

Ниже перечислены распространенные ошибки, с которыми может столкнуться пользователь.

  • WritebackDailyLimitExceeded. Эта ошибка означает, что квота mmd.zram.writeback.max_bytes_per_day исчерпана. В этом случае mmd приостанавливает запись в режиме ожидания до тех пор, пока не пройдет 24 часа.
  • Process prefetch or writeback failed – ошибка, которая может появиться в logcat при сбое ioctl. Распространенные причины:
    • EBADF или ESRCH: целевой процесс завершился до того, как mmd смог отправить pidfd в ядро.
    • ENOSPC: раздел резервного хранилища заполнен или очередь устройства loop исчерпана.
  • ZRAM не настроен. Если mmd не удается настроить ZRAM при загрузке, возможно, устаревшие скрипты swapon_all или скрипты инициализации поставщика заблокировали /dev/block/zram0 до того, как mmd смог выполниться.