Les processus d'application sur Android n'existent pas de manière isolée. Les applications s'appuient souvent sur des services fournis par d'autres applications ou par le système lui-même. Lorsqu'un processus se connecte à un autre via une liaison de service, il crée une dépendance qui a un impact profond sur la façon dont le framework Android gère la mémoire.
États des processus et scores OOM
Le framework Android utilise les états des processus pour suivre l'importance de chaque processus en cours d'exécution. Ces états sont ensuite utilisés par OomAdjuster pour attribuer une valeur d'ajustement du score OOM (oom_score_adj), comprise entre -1000 et 1000.
Une valeur oom_score_adj inférieure signifie que le processus est plus important et moins susceptible d'être arrêté par le Low Memory Killer (LMK).
États de processus courants
Le tableau suivant présente certains des états de processus les plus courants et leurs valeurs oom_score_adj typiques. Pour obtenir une liste complète et à jour, consultez android.app.ActivityManager et com.android.server.am.psc.Constants dans le code source Android.
| État du processus (abrégé) | Description | oom_score_adj typique |
|---|---|---|
| PER (Persistent) | Processus système qui doivent toujours être exécutés (par exemple, la téléphonie). | -800 |
| TOP | Processus avec lequel l'utilisateur interagit actuellement. | 0 |
| VIS (Visible) | Le processus a une activité visible (par exemple, derrière une boîte de dialogue translucide). | 100 |
| PERC (Perceptible) | Processus en arrière-plan dont l'utilisateur est conscient (par exemple, la lecture de musique). | 200 |
| FGS | Processus hébergeant un service de premier plan. | 0 à 200 (variable) |
| BTOP (Bound Top) | Processus lié par une application TOP. | 100 |
| BFGS | Service de premier plan lié (généralement lié au système). | 0 |
| PREV (Previous) | Dernier processus dans lequel l'utilisateur se trouvait avant l'actuel. | 700 |
| CACHED | Applications en arrière-plan qui peuvent être arrêtées en toute sécurité. | 900 à 999 |
Impact des liaisons de service
Lorsqu'un processus client (par exemple, une application à l'état TOP) est lié à un service dans un processus serveur, le processus serveur hérite souvent d'une priorité élevée. Cela garantit que le service reste disponible tant que le client en a besoin.

Contrôler l'héritage avec des flags BIND
L'héritage est le comportement par défaut lorsque vous utilisez Context.BIND_AUTO_CREATE.
Toutefois, les développeurs peuvent contrôler l'impact de la liaison sur l'importance du processus cible à l'aide de différents flags dans bindService().
Flags BIND clés pour le score OOM
Les flags suivants sont les plus pertinents lors de la gestion de la pression sur la mémoire à l'échelle du système :
BIND_AUTO_CREATE: flag le plus courant. Il garantit que le processus de service est démarré et maintenu en vie tant que la liaison existe. Par défaut, il élève également la priorité du processus serveur pour qu'elle corresponde à celle du client.BIND_NOT_FOREGROUND: empêche le processus du service cible d'être élevé à la priorité de planification de premier plan (priorité du CPU). Toutefois, il autorise toujours l'élévation de la priorité de la mémoire (oom_score_adj). Cela est utile pour les tâches en arrière-plan qui ne doivent pas concurrencer l'interface utilisateur pour les cycles de CPU, mais qui doivent tout de même être protégées contre l'arrêt.BIND_WAIVE_PRIORITY: flag très puissant qui indique au système de ne pas avoir d'impact sur la priorité de planification ou de gestion de la mémoire du processus cible. Le processus de service sera géré comme s'il s'agissait d'un processus d'arrière-plan normal dans la liste LRU, ce qui le rend éligible à l'arrêt OOM même lorsqu'il est lié.BIND_ABOVE_CLIENT: indique que le service est plus important que l'application cliente elle-même. Lorsque le système doit récupérer de la mémoire, il préfère arrêter l'application cliente avant d'arrêter le service lié. Cette option est "plus puissante" queBIND_AUTO_CREATE, car elle offre une couche de protection supplémentaire pour le service au détriment du client.BIND_NOT_PERCEPTIBLE: réduit l'importance du service cible en dessous du niveauPERCEPTIBLE, ce qui permet au système de récupérer sa mémoire pour faire de la place pour des processus plus critiques et perceptibles par l'utilisateur.
Exercice pratique : observer les effets de la liaison
Nous allons utiliser l'application MemoryLab pour montrer comment une liaison à partir d'une application TOP affecte l'état d'un processus distinct.
1. Lancer MemoryLab
La commande suivante lance l'application. Une fois l'application ouverte, assurez-vous qu'elle reste au premier plan (n'appuyez pas encore sur le bouton "Accueil" et ne changez pas d'application).
adb shell am start -n com.android.memorylab/.MainActivity
2. Identifier les processus
Vérifiez les états des processus avant la liaison. MemoryLab exécute son interface utilisateur principale dans un processus et dispose d'un RemoteService qui s'exécute dans un processus :remote.
adb shell dumpsys activity processes com.android.memorylab
Exemple d'extrait de sortie :
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
Le processus principal com.android.memorylab s'affiche à l'état TOP. Le processus :remote n'est pas encore démarré.
3. Déclencher la liaison
Envoyez une diffusion à l'application pour déclencher la liaison de service :
adb shell am broadcast -a com.android.memorylab.LEAK_BINDER
4. Observer l'état élevé
Vérifiez à nouveau les états des processus :
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
Exemple d'extrait de sortie :
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
Le processus :remote est maintenant en cours d'exécution et à l'état BTOP (Bound TOP) avec un oom_score_adj de 100. Il est beaucoup plus protégé qu'un service d'arrière-plan typique (qui serait à 500 ou plus). La notation
<=Proc{...} indique le processus responsable de cette élévation de priorité.
5. Envoyer en arrière-plan
Appuyez sur le bouton ACCUEIL de l'appareil. Vérifiez à nouveau les états :
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
Exemple d'extrait de sortie :
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
Les deux processus sont passés à un état de priorité inférieure (PREV / oom_score_adj 700), car le processus client n'est plus TOP. (Remarque :
LAST dans le vidage d'état fait référence à l'état interne LAST_ACTIVITY, qui
correspond à PREV dans les résumés de haut niveau.)
Analyser avec procstats
L'outil procstats fournit une vue historique de ces états.
# View stats for MemoryLab over the last hour
adb shell dumpsys procstats --hours 1 com.android.memorylab
Exemple d'extrait de sortie :
* 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%
Ici, Bnd Top indique le pourcentage de temps pendant lequel le processus distant a été lié par une application à l'état TOP.
Capturer et analyser les liaisons avec Perfetto
Alors que dumpsys vous donne un instantané, Perfetto vous permet de voir le moment exact où une liaison se produit et comment le score OOM change en temps réel.
1. Enregistrer une trace
Utilisez une configuration qui inclut linux.process_stats et la catégorie 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. Interroger les transitions de score OOM
À l'aide de PerfettoSQL, vous pouvez voir comment le score OOM du processus distant a changé par rapport au processus d'interface utilisateur :
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. Identifier les événements de liaison
Pour voir exactement quand une dépendance de liaison a été établie et quel processus l'a initiée, utilisez cette requête :
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%';
Liaisons système-application
Le système Android lui-même se lie souvent à des services dans des applications tierces pour fournir des fonctionnalités de base. L'objectif de ces liaisons est souvent de réduire la latence. En maintenant un processus actif et en mémoire, le système évite la surcharge coûteuse d'un "démarrage à froid" (chargement de l'APK, initialisation de l'environnement d'exécution et création de l'objet Application) lorsqu'une interaction utilisateur critique se produit. D'autres liaisons existent pour éviter les démarrages à froid fréquents pour les applications qui doivent gérer des flux d'événements en arrière-plan.
Voici quelques exemples concrets que vous pouvez observer sur un appareil typique :
VoiceInteractor
Les utilisateurs s'attendent à ce qu'un assistant numérique soit intégré à leur système d'exploitation mobile, qu'ils puissent l'appeler instantanément avec un mot clé parlé ou un geste de saisie rapide, et que l'interaction soit fluide et transparente.
Lorsqu'un déclencheur d'assistant se produit (par exemple, le mot clé "OK Google" sur les téléphones Google Pixel), l'assistant numérique doit répondre instantanément. Pour ce faire,
system_server maintient une liaison permanente avec le service
d'interaction vocale sélectionné par l'utilisateur.

Si vous vérifiez les états des processus (par exemple, à l'aide de dumpsys activity processes), vous pouvez voir un processus tel que com.google.android.googlequicksearchbox:interactor à l'état BFGS (Bound Foreground Service), maintenu en vie par une liaison de system_server (UID 1000).
NotificationListenerService
Pour certaines liaisons système-application, l'objectif n'est pas la latence, mais plutôt la prévention des démarrages à froid fréquents.
NotificationListenerService, un service qui reçoit des appels du système lorsque de nouvelles notifications sont publiées ou supprimées, en est un excellent exemple. Un utilisateur de smartphone type peut recevoir des centaines de notifications tout au long de la journée. Si le système se dissociait d'un écouteur de notifications, le processus de cette application passerait probablement à l'état mis en cache et pourrait être arrêté par le LMK.
Lorsque la notification suivante arrive (potentiellement quelques secondes plus tard), le système serait obligé de redémarrer à froid le processus de l'application juste pour diffuser l'événement. Ce cycle constant d'arrêt et de démarrage à froid consommerait beaucoup plus de CPU et de batterie que de simplement maintenir le processus lié et actif en arrière-plan.
Écran "-1" du lanceur (flux d'actualités)
Les applications de lanceur modernes combinent généralement la fonctionnalité de navigation de base (icônes et widgets d'accueil) avec un flux d'actualités disponible sur l'un des écrans du lanceur et intégré de manière transparente à l'expérience utilisateur du lanceur. Le flux d'actualités peut être fourni par une autre application. Par exemple, sur Google Pixel, le lanceur s'intègre à un flux fourni par l'application Google.
Lorsque vous balayez l'écran d'accueil vers la gauche pour afficher le flux d'actualités, la transition doit être fluide. Le lanceur y parvient en se liant à une interface de service dans l'application qui fournit le flux d'actualités et en maintenant cette liaison active tant que le lanceur est actif. Cela permet de conserver le contenu du flux rendu et prêt en mémoire, même lorsque vous ne le regardez pas.
Autres exemples courants
- Le lanceur (HOME_APP_ADJ) : l'application de lanceur (Accueil) dispose de son propre emplacement spécial
dans la liste de priorité. Bien qu'il ne soit pas toujours lié par un service, il reçoit l'attribut
HOME_APP_ADJ(généralement 600). Le système préfère maintenir le lanceur actif, car l'utilisateur y revient fréquemment. En fait, le système préfère arrêter l'application précédemment utilisée (PREV_APP_ADJ = 700) plutôt que le lanceur, car l'arrêt du lanceur entraînerait une expérience utilisateur lente lors de la fermeture d'une application, car l'utilisateur devrait attendre le démarrage à froid du lanceur. - Éditeur de mode de saisie (IME) : lorsque vous tapez, le système se lie à l'application de clavier que vous avez choisie (par exemple, Gboard). Cela maintient le processus du clavier dans un état élevé, même si le clavier est temporairement masqué. Cela garantit que le clavier peut réapparaître instantanément lorsque vous appuyez sur un autre champ de texte.
- Paiements NFC : lorsque vous approchez votre téléphone pour payer, le système se lie au service de paiement NFC (par exemple, Google Wallet). Ces transactions sont souvent soumises à des exigences strictes en temps réel de la part du terminal marchand. Si l'application de paiement devait démarrer à froid, la transaction pourrait expirer et échouer.
Compromis et seuil de performance
Bien que les liaisons soient nécessaires pour les performances et l'exactitude, elles ont un coût pour l'intégrité de la mémoire du système.
- Flexibilité réduite : chaque processus lié est un processus que le LMK ne peut pas arrêter facilement. Cela réduit la "marge" des processus mis en cache que le système peut utiliser pour libérer de la mémoire sous pression.
- Aggravation du seuil de performance : si trop de processus sont liés, le système peut se retrouver avec presque aucun processus d'arrière-plan pouvant être arrêté. Lorsque la pression sur la mémoire augmente, le système "dépasse le seuil de performance" beaucoup plus rapidement, car il est obligé d'arrêter des processus plus importants ou de saturer le cache de pages.
← Localité | ↑ Haut | À l'échelle du système →