Gérer efficacement la mémoire dans les jeux

Sur la plate-forme Android, le système tente d'utiliser autant de mémoire système (RAM) que possible et effectue diverses optimisations de mémoire pour libérer de l'espace si nécessaire. Ces optimisations peuvent avoir un effet négatif sur votre jeu et le ralentir, voire le faire planter. Pour en savoir plus sur ces optimisations, consultez la page Allocation de mémoire entre les processus.

Cette page décrit les opérations à effectuer pour éviter que les problèmes de mémoire insuffisante n'affectent votre jeu.

Répondre à onTrimMemory()

Le système utilise la méthode onTrimMemory() pour avertir l'application que la mémoire est faible et qu'elle risque de planter. C'est souvent le seul avertissement que recevra votre application. Ce rappel présente une latence élevée par rapport au système LMK (Low Memory Killer). Il est donc essentiel d'y répondre rapidement.

En réponse à ce rappel, réduisez la vitesse, le nombre et la taille des allocations. La méthode onTrimMemory() transmet une constante indiquant un niveau de gravité, mais il est conseillé de répondre dès le premier avertissement, car l'allocation peut s'effectuer plus rapidement que la vitesse de réaction de onTrimMemory().

Kotlin

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        when (level) {
            ComponentCallbacks2.TRIM_MEMORY_MODERATE,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> // Respond to low memory condition
            else -> Unit
        }
    }
}

Java

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
    public void onTrimMemory(int level) {
        switch (level) {
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
              // Respond to low memory condition
                break;
            default:
                break;

C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

class LowMemoryTrigger : MonoBehaviour
{
    private void Start()
    {
        Application.lowMemory += OnLowMemory;
    }
    private void OnLowMemory()
    {
        // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets())
    }
}

Utiliser la version bêta de l'API Memory Advice

L'API Memory Advice a été développée comme une alternative à onTrimMemory, qui offre un rappel et une précision bien plus élevés pour prédire les LMK imminents. Pour ce faire, l'API estime la quantité de ressources mémoire utilisées, puis avertit l'application lorsque certains seuils sont dépassés. Elle peut également indiquer directement le pourcentage d'utilisation de la mémoire à votre application. Vous pouvez utiliser l'API Memory Advice comme alternative aux événements onTrimMemory à des fins de gestion de la mémoire.

Pour utiliser l'API Memory Advice, suivez le guide de démarrage.

Ne pas prendre de risque avec les budgets de mémoire

Définissez soigneusement ce budget pour éviter de manquer de mémoire. Voici quelques éléments à prendre en compte :

  • Taille de la RAM physique : les jeux utilisent souvent entre un quart et la moitié de la RAM physique sur l'appareil.
  • Taille maximale de la zRAM : plus la valeur de zRAM est élevée, plus le jeu dispose de mémoire à allouer, si nécessaire. Cette valeur peut varier en fonction de l'appareil. Recherchez SwapTotal dans /proc/meminfo pour la connaître.
  • Utilisation de la mémoire du système d'exploitation : les appareils qui réservent davantage de RAM pour les processus système en laissent moins pour votre jeu. Un tel système arrête les processus de votre jeu avant de mettre fin à ses propres processus.
  • Utilisation de la mémoire pour les applications installées : testez votre jeu sur des appareils contenant de nombreuses applications installées. Les applications de réseaux sociaux et de chat fonctionnent en permanence, ce qui réduit la quantité de mémoire disponible.

S'il vous est impossible de respecter un budget de mémoire prudent, adoptez une approche plus flexible. Si le système rencontre des problèmes de mémoire insuffisante, réduisez la quantité de mémoire utilisée par le jeu. Par exemple, allouez des textures de résolution inférieure ou stockez moins de nuanceurs en réponse à onTrimMemory(). Cette approche dynamique de l'allocation de mémoire nécessite davantage de travail de la part du développeur, en particulier lors de la phase de conception du jeu.

Éviter le thrashing

Le thrashing se produit lorsque la mémoire disponible est faible, mais pas suffisamment faible pour provoquer l'arrêt du jeu. Dans cette situation, kswapd, ayant récupéré les pages dont le jeu a encore besoin, tente de les recharger à partir de la mémoire. Mais comme l'espace est insuffisant, les pages ne cessent d'être remplacées selon un phénomène d'échange continu. Le traçage système signale cette situation sous la forme d'un thread où kswapd s'exécute en continu.

Le thrashing peut se manifester sous la forme de délais de rendu plus importants (parfois une seconde ou plus). Réduisez l'espace mémoire utilisé par le jeu pour résoudre ce problème.

Utiliser les outils disponibles

Android dispose d'un ensemble d'outils permettant de comprendre comment le système gère la mémoire.

Meminfo

Cet outil collecte des statistiques sur la mémoire pour indiquer la quantité de mémoire PSS allouée et les catégories pour lesquelles elle a été utilisée.

Affichez les statistiques meminfo de l'une des manières suivantes :

  • Exécutez la commande adb shell dumpsys meminfo package-name.
  • Utilisez l'appel MemoryInfo à partir de l'API Android Debug.

La valeur PrivateDirty indique la quantité de mémoire RAM disponible dans le processus qui ne peut pas être paginée sur le disque et qui n'est partagée avec aucun autre processus. La majeure partie de cette mémoire devient disponible pour le système lorsque ce processus est arrêté.

Tracepoints pour la mémoire

Les tracepoints pour la mémoire analysent la quantité de mémoire RSS utilisée par votre jeu. Le calcul de l'utilisation de la mémoire RSS est beaucoup plus rapide que pour la mémoire PSS. Grâce à cette rapidité, les valeurs RSS offrent une meilleure visibilité sur l'évolution de la quantité de mémoire, ce qui permet des mesures plus précises des pics d'utilisation de la mémoire. Par conséquent, il est plus facile d'identifier les moment du jeu susceptibles d'entraîner des insuffisances de mémoire.

Perfetto et les traces allongées

Perfetto est une suite d'outils permettant de collecter des informations sur les performances et la mémoire d'un appareil, puis de les afficher dans une interface utilisateur Web. Comme il prend en charge les traces allongées de longueur variable, cela vous permet de suivre l'évolution des valeurs RSS au fil du temps. Vous pouvez également effectuer des requêtes SQL sur les données qu'il produit pour analyser les valeurs hors connexion. Activez les longues traces à partir de l'application de traçage système. Assurez-vous que la catégorie memory:Memory est activée pour la trace.

Heapprofd

Le daemon heapprofd est un outil de suivi de mémoire intégré à Perfetto. Il peut vous aider à détecter les fuites de mémoire en indiquant où la mémoire a été allouée à l'aide de malloc. heapprofd peut être démarré à l'aide d'un script Python. Comme cet outil consomme peu de ressources, il n'affecte pas les performances comme certains autres outils tels que le débogage malloc.

bugreport

bugreport est un outil de journalisation qui vous permet de savoir si votre jeu a planté ou non en raison d'un manque de mémoire. La sortie de l'outil est beaucoup plus détaillée que celle de Logcat. Il est utile pour le débogage de la mémoire, car il indique si votre jeu a planté en raison d'un manque de mémoire ou s'il a été arrêté par le LMK.

Pour en savoir plus, consultez Capturer et lire les rapports de bug.