Приложения на Android не существуют изолированно. Часто они зависят от сервисов, предоставляемых другими приложениями или самой системой. Когда один процесс соединяется с другим через Service Binding , это создает зависимость, которая оказывает существенное влияние на то, как платформа Android управляет памятью.
Состояния процесса и показатели нехватки памяти
В Android-фреймворке используются состояния процессов для отслеживания важности каждого запущенного процесса. Затем эти состояния используются компонентом OomAdjuster для присвоения значения корректировки оценки нехватки памяти ( oom_score_adj ), варьирующегося от -1000 до 1000.
Более низкое oom_score_adj означает, что процесс более важен и с меньшей вероятностью будет завершен механизмом Low Memory Killer (LMK).
Общие состояния процесса
В следующей таблице показаны некоторые из наиболее распространенных состояний процесса и их типичные значения oom_score_adj . Полный и актуальный список см. в файлах android.app.ActivityManager и com.android.server.am.psc.Constants в исходном коде Android.
| Состояние процесса (сокр.) | Описание | Типичная oom_score_adj |
|---|---|---|
| PER (Постоянный) | Системные процессы, которые должны работать постоянно (например, телефония). | -800 |
| ВЕРШИНА | Процесс, с которым пользователь в данный момент взаимодействует. | 0 |
| ВИС (Видимый) | Процесс включает в себя видимую активность (например, за полупрозрачным диалогом). | 100 |
| PERC (ощутимый) | Фоновый процесс, о котором пользователь знает (например, воспроизведение музыки). | 200 |
| ФГС | Процесс, в котором размещена служба переднего плана. | От 0 до 200 (варьируется) |
| BTOP (Bound Top) | Процесс, связанный с приложением TOP. | 100 |
| БФГС | Связанная служба переднего плана (обычно привязанная к системе). | 0 |
| ПРЕДЫДУЩИЙ (Previous) | Последний процесс, в котором пользователь находился перед текущим. | 700 |
| КЭШИРОВАНО | Фоновые приложения, которые можно безопасно завершить. | 900–999 |
Влияние привязки сервисов
Когда клиентский процесс (например, приложение в состоянии TOP ) подключается к службе в серверном процессе, серверный процесс часто наследует повышенный приоритет. Это гарантирует, что служба останется доступной до тех пор, пока она необходима клиенту.

Управление наследованием с помощью флагов BIND
Наследование является поведением по умолчанию при использовании Context.BIND_AUTO_CREATE . Однако разработчики могут управлять тем, как привязка влияет на важность целевого процесса, используя различные флаги в bindService() .
Ключевые флаги BIND для оценки нехватки памяти (OOM).
При управлении нехваткой памяти в масштабах всей системы наиболее актуальны следующие флаги:
-
BIND_AUTO_CREATE: Наиболее распространенный флаг. Он гарантирует запуск и поддержание работоспособности процесса службы до тех пор, пока существует привязка. По умолчанию он также повышает приоритет процесса сервера до уровня клиента. -
BIND_NOT_FOREGROUND: Предотвращает повышение приоритета планирования процесса целевой службы до приоритета планирования на переднем плане (приоритет ЦП). Однако при этом сохраняется возможность повышения приоритета памяти (oom_score_adj). Это полезно для фоновых задач, которые не должны конкурировать с пользовательским интерфейсом за ресурсы ЦП, но при этом должны быть защищены от завершения. -
BIND_WAIVE_PRIORITY: Очень важный флаг, который указывает системе не влиять на приоритет планирования или управления памятью целевого процесса. Сервисный процесс будет управляться как обычный фоновый процесс в списке LRU, что делает его доступным для завершения из-за нехватки памяти даже во время привязки. -
BIND_ABOVE_CLIENT: Указывает, что служба важнее самого клиентского приложения. Когда системе необходимо освободить память, она предпочтет завершить работу клиентского приложения, а не связанной службы. Это «более надежный» вариант, чемBIND_AUTO_CREATEпоскольку он обеспечивает дополнительный уровень защиты службы за счет клиента. -
BIND_NOT_PERCEPTIBLE: Снижает важность целевой службы до уровня нижеPERCEPTIBLE, позволяя системе освободить память для более важных процессов, воспринимаемых пользователем.
Практическое занятие: наблюдение за эффектами связывания.
Мы воспользуемся приложением MemoryLab , чтобы продемонстрировать, как привязка из приложения TOP влияет на состояние отдельного процесса.
1. Запустите MemoryLab
Следующая команда запускает приложение. После открытия убедитесь, что приложение остается на переднем плане (пока не нажимайте кнопку «Домой» и не переключайтесь между приложениями).
adb shell am start -n com.android.memorylab/.MainActivity
2. Идентификация процессов
Перед привязкой проверьте состояние процесса. MemoryLab запускает свой основной пользовательский интерфейс в одном процессе, а RemoteService работает в процессе с параметром :remote .
adb shell dumpsys activity processes com.android.memorylab
Пример выходного фрагмента:
Process OOM control (154 total, non-act at 7, non-svc at 7):
Proc #0: fg T/A/TOP LCMNFUATI t: 0 13470:com.android.memorylab/u0a417 (top-activity)
oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0
state: cur=TOP set=TOP lastRss=0.00 lastCachedRss=0.00
В состоянии TOP вы увидите основной процесс com.android.memorylab . Процесс :remote еще не запущен.
3. Привязка триггера
Отправьте широковещательное сообщение в приложение, чтобы активировать привязку сервиса:
adb shell am broadcast -a com.android.memorylab.LEAK_BINDER
4. Наблюдайте повышенное состояние
Проверьте еще раз состояния процесса:
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
Пример выходного фрагмента:
Proc # 1: vis F/ /BTOP ---NFUATI t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100
state: cur=BTOP set=BTOP lastRss=0.00 lastCachedRss=0.00
Процесс :remote теперь запущен и находится в состоянии BTOP (Bound TOP) с oom_score_adj равным 100. Это значительно более защищено, чем типичная фоновая служба (которая имела бы приоритет 500 или выше). Обозначение <=Proc{...} показывает, какой процесс отвечает за это повышение приоритета.
5. Отправить в фоновый режим
Нажмите кнопку HOME на устройстве. Еще раз проверьте состояния:
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
Пример выходного фрагмента:
Proc # 2: prev b/ /LAST --------I t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=0.00 lastCachedRss=0.00
Proc # 1: prev b/ /LAST --------I t: 0 13470:com.android.memorylab/u0a417 (previous)
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=209MB lastCachedRss=0.00
Теперь оба процесса перешли в состояние с более низким приоритетом ( PREV / oom_score_adj 700 ), поскольку клиентский процесс больше не является TOP . (Примечание: LAST в дампе состояния относится к внутреннему состоянию LAST_ACTIVITY , которое соответствует PREV в сводках высокого уровня).
Анализ с помощью procstats
Инструмент procstats предоставляет исторический обзор этих штатов.
# View stats for MemoryLab over the last hour
adb shell dumpsys procstats --hours 1 com.android.memorylab
Пример выходного фрагмента:
* com.android.memorylab / u0a417 / v37:
* Prc com.android.memorylab / u0a417 / v37:
TOTAL: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
Top: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
* Prc com.android.memorylab:remote / u0a417 / v37:
TOTAL: 0.19%
Bnd Top: 0.19%
Здесь Bnd Top указывает процент времени, которое удаленный процесс провел в состоянии TOP , будучи привязанным к приложению.
Захват и анализ привязок с помощью Perfetto
В то время как dumpsys предоставляет моментальный снимок состояния, Perfetto позволяет увидеть точный момент возникновения привязки и то, как в реальном времени изменяется оценка нехватки памяти (OOM).
1. Запишите трассировку
Используйте конфигурацию, включающую linux.process_stats и категорию am atrace:
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/service_bindings.perfetto-trace <<EOF
buffers: { size_kb: 65536 }
data_sources: {
config {
name: "linux.process_stats"
process_stats_config { proc_stats_poll_ms: 100 }
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config { ftrace_events: "am/am_proc_bound" }
}
}
duration_ms: 15000
EOF
2. Запрос переходов оценки OOM
С помощью PerfettoSQL можно увидеть, как изменился показатель нехватки памяти (OOM) удаленного процесса относительно процесса пользовательского интерфейса:
SELECT ts, p.name, value AS oom_score_adj
FROM counter c
JOIN process_counter_track t ON c.track_id = t.id
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.memorylab%'
AND t.name = 'oom_score_adj'
ORDER BY ts;
3. Определите события связывания.
Чтобы точно узнать, когда была установлена зависимость привязки и какой процесс её инициировал, используйте следующий запрос:
SELECT
s.ts,
p.name AS process_name,
t.name AS thread_name,
s.name AS slice_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t USING (utid)
JOIN process p USING (upid)
WHERE s.name LIKE 'bindService:{com.android.memorylab%';
Привязки системы к приложению
Сама система Android часто использует привязки к сервисам сторонних приложений для обеспечения основной функциональности. Зачастую целью таких привязок является снижение задержки . Поддерживая процесс активным и в памяти, система избегает дорогостоящих накладных расходов, связанных с «холодным запуском» (загрузка APK-файла, инициализация среды выполнения и создание объекта Application) при критически важном взаимодействии с пользователем. Существуют и другие привязки, предотвращающие частые «холодные запуски» для приложений, которым необходимо обрабатывать потоки фоновых событий.
Вот несколько реальных примеров, которые вы можете наблюдать на типичном устройстве:
VoiceInteractor
Пользователи ожидают, что цифровой помощник будет встроен в операционную систему их телефона, что его можно будет мгновенно вызвать с помощью произнесенного ключевого слова или быстрого жеста ввода, а также что взаимодействие будет плавным и бесшовным.
Когда срабатывает триггер голосового помощника (например, ключевое слово «OK Google» на телефонах Google Pixel), цифровой помощник должен отреагировать мгновенно. Для этого system_server поддерживает постоянную привязку к службе голосового взаимодействия , выбранной пользователем.

Если вы проверите состояния процессов (например, с помощью dumpsys activity processes ), вы можете увидеть процесс типа com.google.android.googlequicksearchbox:interactor в состоянии BFGS (Bound Foreground Service), поддерживаемый привязкой от system_server (UID 1000).
NotificationListenerService
Для некоторых системных привязок к приложениям цель состоит не в снижении задержки, а в предотвращении частых «холодных» перезапусков. Ярким примером является NotificationListenerService — служба, которая получает вызовы от системы при отправке или удалении новых уведомлений. Типичный пользователь смартфона может получать сотни уведомлений в течение дня. Если система отключится от обработчика уведомлений, процесс этого приложения, скорее всего, перейдет в кэшированное состояние и может быть завершен LMK.
Когда придёт следующее уведомление — возможно, через несколько секунд — системе придётся заново запускать процесс приложения, чтобы доставить это событие. Этот постоянный цикл завершения и перезапуска будет потреблять гораздо больше ресурсов процессора и заряда батареи, чем просто поддержание процесса в фоновом режиме.
Экран "-1" в лаунчере (лента новостей)
Современные приложения-лаунчеры обычно объединяют основные функции навигации (значки на главном экране и виджеты) с новостной лентой, доступной на одном из экранов лаунчера и органично интегрированной в пользовательский интерфейс лаунчера. Новостная лента может предоставляться другим приложением. Например, на Google Pixel лаунчер интегрируется с лентой, предоставляемой приложением Google.
Когда вы проводите пальцем влево по главному экрану, чтобы просмотреть новостную ленту, переход должен быть плавным. Лаунчер достигает этого, привязываясь к сервисному интерфейсу в приложении, который предоставляет новостную ленту, и поддерживая эту привязку активной до тех пор, пока лаунчер активен. Это позволяет сохранять содержимое ленты в памяти, даже когда вы его не просматриваете.
Другие распространенные примеры
- Лаунчер (HOME_APP_ADJ) : Приложение-лаунчер (Home) имеет свой собственный слот в списке приоритетов. Хотя оно не всегда привязано к службе, ему присваивается значение
HOME_APP_ADJ(обычно 600 ). Система предпочитает поддерживать работу лаунчера, поскольку пользователь часто к нему возвращается. Фактически, система предпочла бы завершить работу ранее использованного приложения (PREV_APP_ADJ = 700), чем завершить работу лаунчера, поскольку завершение работы лаунчера приведет к замедлению работы при выходе из любого приложения, так как пользователю придется ждать, пока лаунчер перезапустится. - Редактор методов ввода (IME) : Во время набора текста система привязывается к выбранному вами приложению клавиатуры (например, Gboard). Это позволяет поддерживать процесс клавиатуры в режиме повышенных прав, даже если клавиатура временно скрыта. Это гарантирует мгновенное повторное появление клавиатуры при нажатии на другое текстовое поле.
- Платежи NFC : Когда вы прикладываете телефон для оплаты, система подключается к сервису NFC-платежей (например, Google Wallet). Эти транзакции часто предъявляют строгие требования к времени выполнения со стороны торгового терминала. Если платежному приложению потребуется «холодный запуск», транзакция может завершиться по истечении времени ожидания и не состояться.
Компромиссы и обрыв производительности
Хотя привязки необходимы для производительности и корректной работы, они негативно сказываются на состоянии памяти системы.
- Сниженная гибкость : Каждый связанный процесс — это процесс, который LMK не может легко завершить. Это уменьшает «резерв» кэшированных процессов, который система может использовать для освобождения памяти под нагрузкой.
- Усугубление проблемы резкого снижения производительности : если задействовано слишком много процессов, система может обнаружить, что у нее практически не осталось фоновых процессов, которые можно завершить. При увеличении нагрузки на память система «сорвется с обрыва производительности» гораздо быстрее, поскольку ей придется завершать более важные процессы или использовать кэш страниц.
← Местоположение | ↑ Вверх | В масштабах всей системы →