À propos de la gestion de la mémoire

L'optimisation de la mémoire est essentielle pour offrir des expériences de jeu stables et performantes sur Android. Ce guide explique pourquoi l'efficacité de la mémoire est importante, comment le système d'exploitation Android gère les limites de mémoire des processus et comment les nouvelles métriques de mémoire de la Google Play Console vous aident à surveiller et à améliorer la qualité technique de votre jeu.

L'importance de l'optimisation de la mémoire

L'optimisation de la mémoire de votre jeu est essentielle pour fidéliser les joueurs, étendre la compatibilité avec les appareils et respecter les normes de qualité de la plate-forme :

  • Prévention des démarrages à froid (expérience utilisateur et rétention) : lorsqu'un joueur quitte temporairement votre jeu (par exemple, pour répondre à une notification ou consulter un message), le système d'exploitation place le processus du jeu en arrière-plan. Si l'espace mémoire utilisé par le jeu en arrière-plan est trop élevé, le Low Memory Killer (LMK) du système donne la priorité à l'arrêt du processus du jeu pour récupérer de la RAM pour les tâches au premier plan. La prochaine fois que l'utilisateur reprendra sa partie, au lieu d'une reprise à chaud fluide et instantanée, le jeu devra effectuer un démarrage à froid long, en rechargeant complètement les assets graphiques lourds, l'audio et les binaires du moteur de jeu depuis l'espace de stockage. En limitant l'utilisation de la mémoire en arrière-plan, vous évitez ces arrêts silencieux en arrière-plan, ce qui préserve l'état de l'utilisateur et permet aux joueurs de reprendre leur session immédiatement. Pour en savoir plus sur le comportement du LMK du système, consultez le guide Android Vitals – Low memory killers.
  • Stabilité de l'écosystème et de l'appareil : une utilisation inefficace de la mémoire et des fuites de mémoire dégradent l'état général du système. Lorsque la mémoire système est insuffisante, le système est soumis à une forte pression, ce qui entraîne une baisse de la fréquence d'images, des saccades de l'interface utilisateur et des problèmes audio. Si la pression sur la mémoire est trop importante, le Low Memory Killer (LMK) du système met fin de manière agressive aux processus en arrière-plan, ce qui force les autres applications à subir des démarrages à froid lents et à perdre l'état de l'utilisateur lorsque les joueurs passent d'une tâche à l'autre.
  • Arrêts au niveau de la plate-forme : à partir d'Android 17 (niveau d'API 37), le système est plus proactif pour arrêter les processus qui utilisent trop de mémoire. Si l'empreinte de votre jeu est trop élevée, l'OS peut mettre fin à son processus de manière abrupte sans générer de trace de la pile standard.
  • Compatibilité des appareils : alors que les appareils phares disposent de 12 à 16 Go de RAM, une grande partie des joueurs dans le monde utilisent des appareils dotés de 4 ou 6 Go de RAM. Une bonne gestion de la mémoire garantit que votre jeu reste accessible et réactif sur tous les niveaux de matériel, sans nécessiter de packages d'assets complexes et distincts.

Comprendre la mémoire dans Android

Pour concevoir des stratégies efficaces de budgétisation de la mémoire, les développeurs doivent comprendre comment la plate-forme Android gère la mémoire physique et comment elle mesure l'empreinte active de votre jeu.

Concepts fondamentaux de la mémoire Android

Pour en savoir plus sur les concepts fondamentaux de la gestion de la mémoire au niveau de la plate-forme, consultez la documentation officielle Présentation de la gestion de la mémoire. Cette ressource couvre quatre domaines architecturaux :

  • Présentation de la mémoire : Android utilise la pagination et la mise en correspondance de mémoire (mmap) pour gérer la RAM. Il n'est pas compatible avec un fichier d'échange traditionnel sur disque. À la place, il s'appuie sur la compression de pages (à l'aide de zRAM) et la récupération de pages pour libérer de la mémoire physique.
  • Allocation de mémoire entre les processus : Android partage la RAM sur l'ensemble du système. Il attribue des tas spécifiques pour l'exécution de la machine virtuelle Dalvik ou ART, tout en permettant aux environnements de développement natifs (comme les moteurs de jeu C++) de demander de la mémoire au tas du système natif.
  • Gestion de la mémoire des applications : fonctionnant selon un modèle multiprocessus, Android s'attend à ce que les applications surveillent l'état de leur cycle de vie de manière dynamique et libèrent volontairement les ressources inutiles (telles que les graphiques et les bitmaps non mis en cache) pour préserver l'état du système.
  • Présentation des processus et des threads : le système classe les processus dans une hiérarchie en fonction de leur visibilité et de leur importance perçues par l'utilisateur, ce qui détermine les processus qui sont conservés et ceux qui sont arrêtés en premier lorsque la mémoire est faible.

La métrique "Espace mémoire total utilisé"

Le limiteur de mémoire d'Android 17 au niveau de la plate-forme évalue la consommation des processus à l'aide de l'espace mémoire total utilisé plutôt que de la taille résidente totale (RSS) ou de la taille de la mémoire virtuelle.

Espace mémoire total utilisé = RSS anonyme (RssAnon) + Swap non compressé (VmSwap)

Pour éviter que les jeux ne dépassent les limites de la plate-forme, vous devez comprendre exactement ce que ces métriques représentent au niveau du système. Pour en savoir plus sur ces métriques, les allocations de RAM physique et la gestion des pages sauvegardées par des fichiers, consultez Comprendre les métriques RSS et d'échange dans le guide Surveiller l'utilisation de la mémoire.

Contraintes de mémoire

Pour maintenir la stabilité du système et s'assurer que les applications ne consomment pas trop de ressources, la plate-forme Android gère les limites de mémoire pour les processus en cours d'exécution.

Limiteur de mémoire dans Android 17 et versions ultérieures

Android 17 (niveau d'API 37) et versions ultérieures gèrent des limites de mémoire strictes par application à l'aide de Linux cgroup v2 pour éviter que des applications individuelles ne provoquent une instabilité à l'échelle du système. Pour en savoir plus sur l'implémentation technique, consultez le guide du limiteur de mémoire AOSP et l'article de blog Prioritizing Memory Efficiency: Essential Steps for Android 17 (Priorité à l'efficacité de la mémoire : étapes essentielles pour Android 17).

  • Mécanisme : le limiteur de mémoire surveille tous les processus d'application et attribue des limites de manière dynamique en fonction de l'état du cycle de vie du processus :
    • Processus visibles (premier plan) : les processus d'application qui affichent actuellement une UI sont censés exécuter un ensemble de travail de ressources plus important et bénéficient d'une limite plus généreuse.
    • Processus non visibles (arrière-plan ou services) : les processus d'application qui effectuent un travail actif sans afficher d'UI sont limités à un budget plus restreint et plus restrictif.
  • Attributs du noyau : le service repose sur deux attributs principaux :
    • memory.high : limite souple. En cas de dépassement, le noyau limite le processus et tente de récupérer de la mémoire de manière agressive. Cette récupération peut entraîner une dégradation des performances du jeu.
    • memory.swap.max : gère une limite stricte de l'espace d'échange ou de zRAM que le processus peut utiliser.
  • Comportement d'arrêt : si un processus continue d'allouer de la mémoire anonyme au-delà de memory.high et épuise sa capacité d'échange, les allocations échouent et l'OS arrête le processus de manière silencieuse. Cet arrêt est consigné à l'aide de ApplicationExitInfo sous le motif de sortie Limiteur de mémoire (disponible à partir d'Android 17, 26Q4).

Nouvelles limites de mémoire dans les données vitales de la Play Console

Pour aider les développeurs à identifier de manière proactive les problèmes de mémoire, Google Play introduit de nouvelles métriques dans Android Vitals de la Play Console. La Play Console suit le 90e centile (P90) de l'espace mémoire utilisé par le RSS anonyme et l'espace d'échange de vos sessions de jeu pour identifier les valeurs aberrantes extrêmes.

Les seuils d'avertissement et de mesure d'application sont ajustés en fonction de la capacité de RAM physique de l'appareil et des états du processus. Ces limites s'appliquent en deux phases distinctes.

Pour obtenir des consignes détaillées et connaître les limites de mémoire, consultez Android Vitals : quels sont les seuils de comportement insatisfaisant ?.

Services perçus par l'utilisateur

Les services perceptibles sont des processus en arrière-plan critiques que le système Android considère comme perceptibles par l'utilisateur. Cet état englobe tous les processus en cours d'exécution :

  • Services de premier plan (FGS)
  • Tâches accélérées
  • Tâches de transfert de données déclenchées par l'utilisateur
  • Services liés au système ou services liés par d'autres applications

Étant donné que les services perceptibles sont conçus pour des tâches critiques de longue durée en arrière-plan, ils sont très sensibles aux fuites cumulatives du cycle de vie. Les limites de mémoire de la plate-forme Android traitent cet état comme n'étant pas au premier plan. Cela signifie que si votre jeu est en arrière-plan, mais qu'il continue d'exécuter un service perceptible, il est soumis aux limites de mémoire plus strictes en arrière-plan ou de service indiquées sur la page du centre d'aide Play. Les jeux doivent supprimer de manière agressive les éléments inutiles lorsqu'ils passent du premier plan à un état de service perceptible en arrière-plan.

Exigences de R8

Pour minimiser la taille du bytecode et réduire la surcharge de base des processus Java, la Google Play Console évalue l'optimisation du code dans le cadre de ses consignes relatives à la qualité des applications. Pour en savoir plus sur les exigences et les limites d'optimisation applicables aux jeux, consultez Optimisation du code DEX dans la documentation Android Vitals.

Pour configurer R8 dans un projet Android standard, suivez le guide Activer l'optimisation des applications. Pour activer les paramètres avancés de minification et d'optimisation, consultez Utiliser R8 en mode complet. Pour identifier les règles qui empêchent R8 d'obscurcir les classes ou de supprimer le code mort, utilisez l'analyseur de configuration R8.

Si vous utilisez un moteur de jeu, le comportement par défaut de R8 varie selon le moteur et la version :

  • Unity : la minimisation du code est désactivée par défaut. Vous devez donc l'activer dans les paramètres de votre projet. Pour en savoir plus sur la compatibilité des versions et les étapes de configuration, consultez Réduire et optimiser le code Unity avec R8.
  • Unreal Engine : à partir d'UE 4.25, R8 est activé par défaut dans les builds de version, ce qui vous oblige à gérer les règles de conservation pour les ponts et les plug-ins JNI. Pour en savoir plus sur les versions et la configuration UPL, consultez Réduire et optimiser le code Unreal Engine avec R8.

Exigences concernant les bitmaps

Les bitmaps représentent une part importante de l'utilisation de la mémoire dans les jeux haute fidélité modernes. Étant donné que les données de pixels bitmap sont stockées directement dans le tas de mémoire natif non géré sur Android 8.0 (niveau d'API 26) et versions ultérieures, le chargement d'images non optimisées peut pousser les processus au-delà des seuils de mémoire de la plate-forme. Pour connaître les bonnes pratiques concernant la mise à l'échelle et la mise en cache des images, consultez Optimiser l'utilisation des images.

Surveiller l'utilisation de la mémoire

Pour optimiser efficacement la mémoire de votre jeu, vous devez d'abord comprendre comment la plate-forme Android mesure son empreinte. Android 17 met à jour la métrique de mémoire pour suivre la somme de la mémoire RSS anonyme (RssAnon) et de l'espace d'échange non compressé (VmSwap), à l'exclusion de la mémoire sauvegardée par fichier ou privée du GPU. Ce guide explique comment utiliser des outils au niveau du système tels que Perfetto et meminfo, implémenter des API de diagnostic telles que ProfilingManager et onTrimMemory, et extraire des allocations de mémoire précises dans Unity et Unreal Engine. Découvrez comment profiler précisément votre jeu et éviter les saccades de performances associées à l'interrogation traditionnelle de la mémoire d'exécution.

Pour en savoir plus, consultez Surveiller l'utilisation de la mémoire.

Stratégies de réduction de la mémoire

Bien que les moteurs de jeu simplifient le développement multiplate-forme, leur gestion de la mémoire par défaut peut déclencher des limites de mémoire au niveau de l'OS. Cette page détaille les étapes d'optimisation pratiques spécialement adaptées à Unity et Unreal Engine. Découvrez pourquoi l'utilisation de onTrimMemory basé sur Java peut entraîner des blocages dans Unity et comment utiliser des rappels de cycle de vie natifs à la place. Vous découvrirez également des optimisations clés au niveau des assets, comme l'utilisation de la compression de texture ASTC 8x8 et la configuration des déchargements d'assets, pour que votre jeu fonctionne de manière fluide sur tous les niveaux de matériel.

Pour en savoir plus, consultez Réduire l'utilisation de la mémoire.