Inspecter l'utilisation de mémoire de votre application avec le Profileur de mémoire

Le Profileur de mémoire est un composant du Profileur Android qui permet d'identifier les problèmes de fuite ou de saturation de la mémoire susceptibles d'entraîner des saccades, un gel de l'écran ou même un plantage de l'application. Il affiche un graphique en temps réel reflétant l'utilisation de la mémoire de votre application, et vous permet de capturer une empreinte, de forcer la récupération et de suivre les allocations de mémoire.

Pour ouvrir le Profileur de mémoire :

  1. Cliquez sur Afficher > Fenêtres d'outils > Profileur ou sur Profil  (dans la barre d'outils).
  2. Dans la barre d'outils du Profileur Android, sélectionnez l'appareil et le processus d'application que vous souhaitez profiler. Si vous avez connecté un appareil via USB, mais qu'il ne figure pas dans la liste, assurez-vous d'avoir activé le débogage USB.
  3. Cliquez n'importe où dans la chronologie MÉMOIRE pour ouvrir le Profileur de mémoire.

Vous pouvez également inspecter la mémoire utilisée par votre application à partir de la ligne de commande dumpsys et examiner les événements de récupération de mémoire dans Logcat.

Pourquoi profiler la mémoire d'une application ?

Android fournit un environnement mémoire géré. Lorsqu'il détermine que votre application n'utilise plus certains objets, le récupérateur libère la mémoire inutilisée pour la rendre disponible. La façon dont Android détecte la mémoire inutilisée est constamment améliorée, mais toutes les versions ont un point commun : à un moment donné, le système doit suspendre brièvement votre code. La plupart du temps, ces pauses sont imperceptibles. Toutefois, si votre application alloue de la mémoire plus rapidement que le système ne peut la récupérer, son fonctionnement peut être retardé pendant que le collecteur libère suffisamment de mémoire pour satisfaire la demande. Certains frames peuvent être ignorés pendant ce délai, et votre application peut sembler lente à réagir.

En cas de fuite, votre application peut monopoliser la mémoire en arrière-plan, même si elle ne présente pas de ralentissement visible. Ce comportement peut affecter les performances du reste de la mémoire du système, en imposant des événements de récupération de mémoire inutiles. Le système devra éventuellement mettre fin au processus de votre application pour récupérer la mémoire. Ensuite, lorsque l'utilisateur rouvre votre application, celle-ci doit redémarrer complètement.

Le Profileur de mémoire vous permet d'éviter ces problèmes. Procédez comme suit :

  • Dans la chronologie, recherchez des schémas d'allocation de mémoire indésirables et susceptibles d'entraîner des problèmes de performances.
  • Videz le tas de mémoire Java pour voir quels objets consomment de la mémoire à différents moments. Multiplier les empreintes de la mémoire au cours d'une période prolongée peut vous aider à identifier les fuites.
  • Enregistrez l'allocation de mémoire lors d'interactions utilisateur normales et extrêmes pour identifier précisément les points où votre code alloue trop d'objets en peu de temps, ou alloue les objets produisant les fuites.

Pour en savoir plus sur les pratiques de programmation permettant de réduire l'utilisation de mémoire de votre application, consultez la page Gérer la mémoire de votre application.

Présentation du Profileur de mémoire

Lorsque vous ouvrez le Profileur de mémoire pour la première fois, vous pouvez consulter une chronologie détaillée de l'utilisation de la mémoire et accéder à des outils permettant de forcer la récupération, de capturer une empreinte ou d'enregistrer les allocations de mémoire.

Figure 1 : Le Profileur de mémoire.

La vue par défaut du Profileur de mémoire (présentée dans la figure 1) comprend les éléments suivants :

  1. Bouton permettant de forcer la récupération de mémoire.
  2. Bouton permettant de capturer une empreinte de la mémoire.

    Remarque : Le bouton permettant d'enregistrer les allocations de mémoire apparaît à droite du bouton de capture d'empreinte, mais ne s'affiche que si l'appareil connecté exécute Android 7.1 (niveau d'API 25) ou une version antérieure.

  3. Menu déroulant permettant de spécifier la fréquence à laquelle le profileur capture les allocations de mémoire. Sélectionner l'option appropriée peut vous aider à améliorer les performances de l'application lors du profilage.
  4. Boutons permettant d'effectuer un zoom avant ou arrière sur la timeline.
  5. Bouton permettant d'accéder aux données de la mémoire en direct.
  6. Chronologie des événements, qui affiche les états d'activité, les événements d'entrée utilisateur et les événements de rotation d'écran.
  7. La chronologie d'utilisation de la mémoire comprend les éléments suivants :
    • Un graphique empilé représentant la quantité de mémoire utilisée (indiquée sur l'axe vertical) par chaque catégorie (identifiée par le code couleur au-dessus du graphique).
    • Une ligne en pointillé indiquant le nombre d'objets alloués (également sur l'axe vertical, à droite).
    • Une icône pour chaque événement de récupération de mémoire.

Toutefois, si vous utilisez un appareil exécutant Android 7.1 ou une version antérieure, certaines données de profilage ne sont pas visibles par défaut. Si vous voyez un message indiquant que le profilage avancé n'est pas disponible pour le processus sélectionné, vous devez activer le profilage avancé pour afficher les éléments suivants :

  • Chronologie des événements
  • Nombre d'objets alloués
  • Événements de récupération de mémoire

Sur Android 8.0 et versions ultérieures, le profilage avancé est toujours activé pour les applications débogables.

Méthode de comptabilisation de la mémoire

Les chiffres affichés en haut du Profileur de mémoire (figure 2) sont basés sur toutes les pages de mémoire privées réservées par votre application et connues du système Android. Ce décompte n'inclut pas les pages partagées avec le système ou avec d'autres applications.

Figure 2 : La légende affichant les quantités de mémoire utilisées en haut de la fenêtre du Profileur de mémoire.

Les catégories de mémoire mesurées sont les suivantes :

  • Java : la mémoire allouée aux objets issus du code Java ou Kotlin.
  • Native : la mémoire allouée aux objets issus du code C ou C++.

    Votre application peut occuper de la mémoire native même si vous n'utilisez pas de C++, car le framework Android l'utilise pour traiter automatiquement différentes tâches en votre nom (notamment pour gérer les images et autres éléments graphiques, même si votre code est écrit en Java ou en Kotlin).

  • Graphique : la mémoire allouée aux files d'attente de tampon des éléments graphiques afin d'afficher des pixels à l'écran, y compris les surfaces et les textures GL, entre autres. (Notez qu'il s'agit ici de mémoire partagée avec le processeur et non de mémoire graphique dédiée.)

  • Pile : la mémoire utilisée par les piles natives et Java, qui dépend généralement du nombre de threads exécutés par votre application.

  • Code : la mémoire utilisée pour le code et les ressources de votre application (bytecode DEX, code DEX optimisé ou compilé, bibliothèques SO, polices, etc.).

  • Autres : la mémoire utilisée par votre application, que le système ne sait pas comment catégoriser.

  • Objets : le nombre d'objets Java et Kotlin alloués par votre application. Les objets C et C++ ne sont pas pris en compte.

    Si votre appareil exécute Android 7.1 ou une version antérieure, le comptage des allocations ne commence que lorsque le Profileur de mémoire est connecté à votre application en cours d'exécution. Par conséquent, les objets alloués avant le début du profilage ne sont pas pris en compte. Les versions 8.0 et ultérieures d'Android incluent un outil de profilage sur l'appareil, qui effectue le suivi de toutes les allocations. Si votre appareil exécute Android 8.0 ou une version ultérieure, ce nombre représente toujours la totalité des objets Java persistants dans votre application.

Le Profileur de mémoire n'enregistre pas la mémoire de la même manière que l'ancien outil Android Monitor, ce qui peut donner l'impression que votre utilisation de mémoire est plus élevée. Le Profileur de mémoire surveille certaines catégories supplémentaires, ce qui augmentent le total. Cependant, si vous ne vous intéressez qu'à la mémoire du segment Java, la valeur devrait être semblable à celle obtenue avec l'outil précédent. Elle peut cependant ne pas correspondre exactement, car le Profileur prend en compte toutes les pages de mémoire physique allouées au tas de mémoire Java de votre application depuis sa séparation de Zygote. Vous disposez ainsi d'une représentation précise de la quantité de mémoire physique utilisée par votre application.

Afficher les allocations de mémoire

Les allocations de mémoire vous indiquent comment chaque objet Java ou référence JNI de votre mémoire ont été alloués. Plus spécifiquement, le Profileur de mémoire peut afficher les informations suivantes concernant les allocations d'objets :

  • Les types d'objets alloués et l'espace qu'ils occupent.
  • La trace de la pile pour chaque allocation, avec identification du thread.
  • Le code temporel marquant la désallocation des objets (uniquement pour les appareils exécutant Android 8.0 ou une version ultérieure).

Pour enregistrer des allocations Java et Kotlin, sélectionnez Enregistrer des allocations Java/Kotlin, puis Enregistrer. Si l'appareil est équipé d'Android 8 ou version ultérieure, l'UI du Profileur de mémoire passe à un écran distinct affichant l'enregistrement en cours. Vous pouvez interagir avec la mini chronologie située au-dessus de l'enregistrement (par exemple, pour modifier la plage de sélection). Pour terminer l'enregistrement, sélectionnez Arrêter .

Visualisation des allocations Java dans le Profileur de mémoire

Sur Android 7.1 et versions antérieures, le Profileur de mémoire utilise l'ancien mode d'enregistrement des allocations et l'affiche dans la chronologie jusqu'à ce que vous cliquiez sur Stop (Arrêter).

Après avoir sélectionné une région de la chronologie (ou après avoir terminé une session d'enregistrement avec un appareil exécutant Android 7.1 ou une version antérieure), la liste des allocations s'affiche. Les objets sont regroupés par nom de classe et triés par nombre de tas de mémoire.

Pour inspecter l'enregistrement d'allocation, procédez comme suit :

  1. Parcourez la liste pour trouver les objets ayant un nombre de tas de mémoire inhabituellement élevé susceptibles d'indiquer une fuite. Pour trouver des classes connues, cliquez sur l'en-tête de la colonne Nom de la classe pour la trier par ordre alphabétique. Ensuite, cliquez sur le nom d'une classe. Le volet Vue des instances apparaît à droite et affiche chaque instance de cette classe, comme illustré dans la figure 3.
    • Vous pouvez également localiser rapidement les objets en cliquant sur Filtrer  ou en appuyant sur Ctrl+F (Commande+F sur Mac), puis en saisissant un nom de classe ou de package dans le champ de recherche. Vous pouvez également effectuer une recherche par nom de méthode si vous sélectionnez Organiser par pile d'appels dans le menu déroulant. Si vous souhaitez utiliser des expressions régulières, cochez la case Expression régulière. Si votre recherche est sensible à la casse, cochez la case à côté de Respecter la casse.
  2. Dans le volet Vue des instances, cliquez sur une instance. L'onglet Call Stack (Pile d'appel) apparaît en dessous, indiquant où cette instance a été allouée et dans quel thread.
  3. Dans l'onglet Pile d'appels, effectuez un clic droit sur une ligne et sélectionnez Accéder à la source pour ouvrir ce code dans l'éditeur.

Figure 3 : Les détails de chaque objet alloué sont affichés dans la section Vue des instances, à droite.

Vous pouvez utiliser les deux menus situés au-dessus de la liste des objets alloués pour choisir le tas de mémoire à inspecter et comment organiser les données.

Dans le menu de gauche, sélectionnez le tas de mémoire à inspecter :

  • Tas de mémoire par défaut : si aucun tas de mémoire n'est spécifié par le système.
  • Tas de mémoire de l'image de démarrage : l'image de démarrage du système, qui contient les classes préchargées au démarrage. Ces allocations ne seront jamais déplacées ni supprimées.
  • Tas de mémoire de Zygote : le tas de mémoire en copie sur écriture, à partir duquel les processus d'application sont dupliqués dans le système Android.
  • Tas de mémoire de l'application : le tas principal sur lequel votre application alloue de la mémoire.
  • Tas de mémoire JNI : le tas de mémoire où sont allouées puis libérées les références JNI (Java Native Interface).

Dans le menu de droite, choisissez comment organiser les allocations :

  • Trier par classe : pour regrouper les allocations par nom de classe. Il s'agit de l'option par défaut.
  • Trier par package : pour regrouper les allocations par nom de package.
  • Trier par pile d'appels : pour regrouper les allocations dans les piles d'appel correspondantes.

Améliorer les performances de l'application lors du profilage

Par défaut, le Profileur de mémoire échantillonne les allocations de mémoire à intervalles réguliers pour améliorer les performances de l'application lors du profilage. Lorsque vous effectuez des tests sur des appareils exécutant un niveau d'API 26 ou supérieur, vous pouvez modifier ce comportement à partir du menu déroulant Allocation Tracking (Suivi des allocations). Vous disposez des options suivantes :

  • Complet : capture toutes les allocations d'objet en mémoire. Il s'agit du comportement par défaut dans Android Studio 3.2 et ses versions antérieures. Si votre application alloue de nombreux objets, elle risque d'être visiblement ralentie pendant le profilage.
  • Échantillonné : capture les allocations d'objets en mémoire à intervalles réguliers Il s'agit du comportement par défaut actuel, qui a moins d'impact sur les performances de l'application lors du profilage. Les applications qui allouent de nombreux objets sur une courte période peuvent toujours présenter des ralentissements visibles.
  • Désactivé : arrête le suivi de l'allocation de mémoire de l'application.

Afficher les références JNI globales

Java Native Interface (JNI) est un framework qui permet les appels mutuels entre le code Java et le code natif.

Les références JNI étant gérées manuellement par le code natif, il est possible que les objets Java utilisés par le code natif soient conservés pendant trop longtemps. Certains objets du tas de mémoire Java peuvent devenir inaccessibles si une référence JNI est abandonnée sans être explicitement supprimée. Il est également possible d'atteindre la limite de références JNI globales.

Pour résoudre ces problèmes, utilisez la vue Tas de mémoire JNI dans le Profileur de mémoire, afin de parcourir toutes les références JNI globales et de les filtrer par type Java et piles d'appels natives. Ces informations permettent de savoir où et quand les références JNI globales sont créées et supprimées.

Pendant l'exécution de votre application, sélectionnez une partie de la chronologie que vous souhaitez inspecter, puis sélectionnez Tas de mémoire JNI dans le menu déroulant situé au-dessus de la liste des classes. Vous pouvez ensuite inspecter les objets du tas de mémoire comme vous le feriez normalement, et double-cliquer sur les objets dans l'onglet Pile d'appel de l'allocation pour voir où les références JNI sont allouées et libérées dans votre code, comme illustré dans la figure 4.

Figure 4 : Affichage des références JNI globales.

Pour inspecter les allocations de mémoire pour le code JNI, vous devez déployer votre application sur un appareil exécutant Android 8.0 ou une version ultérieure.

Pour plus d'informations sur JNI, consultez les conseils sur JNI.

Profileur de mémoire natif

Le Profileur de mémoire d'Android Studio inclut un profileur de mémoire natif pour les applications déployées sur des appareils physiques et virtuels exécutant Android 10 ou version ultérieure.

Le profileur de mémoire native suit les allocations et désallocations d'objets dans le code natif pendant une période donnée, et peut fournir les informations suivantes :

  • Allocations : le nombre d'objets alloués via malloc() ou via l'opérateur new pendant la période sélectionnée.
  • Désallocations : le nombre d'objets désalloués via free() ou via l'opérateur delete pendant la période sélectionnée.
  • Volume alloué : l'espace mémoire cumulatif (en octets) occupé par les allocations effectuées pendant la période sélectionnée.
  • Volume désalloué : l'espace mémoire cumulatif (en octets) libéré par les désallocations effectuées pendant la période sélectionnée.
  • Total des allocations : la valeur de la colonne Allocations après déduction de celle de la colonne Désallocations.
  • Volume occupé : la valeur de la colonne Volume alloué après déduction de celle de la colonne Volume désalloué.

Profileur de mémoire native

Pour enregistrer des allocations natives sur des appareils exécutant Android 10 ou une version ultérieure, sélectionnez Record native allocations (Enregistrer des allocations natives), puis Save (Enregistrer). L'enregistrement se poursuit jusqu'à ce que vous cliquiez sur Arrêter , après quoi l'UI du Profileur de mémoire passe à un écran distinct affichant l'enregistrement natif.

Bouton "Enregistrer les allocations natives"

Sous Android 9 et versions antérieures, l'option Record native allocations (Enregistrer des allocations natives) n'est pas disponible.

Par défaut, le Profileur de mémoire native utilise un intervalle d'échantillonnage de 32 octets. Chaque fois que 32 octets de mémoire sont alloués, une capture instantanée est enregistrée. Un intervalle réduit génère des instantanés plus fréquents et fournit des données plus précises sur l'utilisation de la mémoire. Un intervalle large fournit des données moins précises, mais consomme moins de ressources sur votre système et améliore les performances lors de l'enregistrement.

Pour modifier l'intervalle d'échantillonnage du Profileur de mémoire native :

  1. Sélectionnez Exécuter > Modifier les configurations.
  2. Sélectionnez votre module d'application dans le volet de gauche.
  3. Cliquez sur l'onglet Profilage, puis saisissez l'intervalle d'échantillonnage dans le champ intitulé Intervalle d'échantillonnage de la mémoire native (octets).
  4. Créez et exécutez à nouveau votre application.

Capturer une empreinte de la mémoire

Une empreinte de la mémoire révèle les objets de votre application qui utilisent la mémoire au moment de la capture. Une empreinte de la mémoire peut vous aider à identifier les fuites de mémoire (surtout après une longue session utilisateur) en affichant les objets qui restent en mémoire alors qu'ils ne devraient plus y être.

Après avoir capturé une empreinte de la mémoire, vous pouvez voir les éléments suivants :

  • Les types d'objets alloués par votre application, et leur nombre.
  • La quantité de mémoire utilisée par chaque objet.
  • L'emplacement dans votre code des références à chacun de ces objets.
  • La pile d'appel pour l'emplacement d'allocation d'un objet. (Pour le moment, les données sur les piles d'appels ne sont disponibles que pour les empreintes de mémoire capturées sous Android 7.1 ou une version antérieure, lorsque la capture a lieu parallèlement à l'enregistrement des allocations.)

Pour capturer une empreinte de la mémoire, sélectionnez Capture heap dump (Capturer l'empreinte de la mémoire), puis Save (Enregistrer). La quantité de mémoire Java utilisée peut augmenter temporairement pendant la capture. C'est normal, car l'opération est effectuée dans le même processus que votre application et nécessite de la mémoire pour collecter les données.

Une fois que le profileur a terminé la capture, l'UI du Profileur de mémoire bascule sur un écran distinct qui affiche l'empreinte de la mémoire.

Figure 5 : Affichage de l'empreinte de la mémoire.

Si vous avez besoin de spécifier plus précisément la capture, vous pouvez appeler dumpHprofData() pour créer une empreinte de mémoire à un point critique du code de votre application.

Dans la liste des classes, les informations suivantes s'affichent :

  • Allocations : le nombre d'allocations dans le segment de mémoire.
  • Volume de mémoire native : la quantité totale de mémoire native utilisée par ce type d'objet (en octets). Cette colonne n'est visible que sur Android 7.0 et les versions ultérieures.

    Certains objets alloués en Java sont inclus à ce volume, car Android utilise de la mémoire native pour certaines classes de framework, telles que Bitmap.

  • Taille superficielle : la quantité totale de mémoire Java utilisée par ce type d'objet (en octets).

  • Taille conservée : le volume total de mémoire conservé pour toutes les instances de cette classe (en octets).

Vous pouvez utiliser les deux menus situés au-dessus de la liste des objets alloués pour choisir l'empreinte de mémoire à inspecter et comment organiser les données.

Dans le menu de gauche, sélectionnez le tas de mémoire à inspecter :

  • Tas de mémoire par défaut : si aucun tas de mémoire n'est spécifié par le système.
  • Tas de mémoire de l'application : le tas principal sur lequel votre application alloue de la mémoire.
  • Tas de mémoire de l'image de démarrage : l'image de démarrage du système, qui contient les classes préchargées au démarrage. Ces allocations ne seront jamais déplacées ni supprimées.
  • Tas de mémoire de Zygote : le tas de mémoire en copie sur écriture, à partir duquel les processus d'application sont dupliqués dans le système Android.

Dans le menu de droite, choisissez comment organiser les allocations :

  • Trier par classe : pour regrouper les allocations par nom de classe. Il s'agit de l'option par défaut.
  • Trier par package : pour regrouper les allocations par nom de package.
  • Trier par pile d'appels : pour regrouper les allocations dans les piles d'appel correspondantes. Cette option ne fonctionne que si vous capturez l'empreinte de la mémoire pendant que vous enregistrez les allocations. Même ainsi, il est probable que des objets du tas de mémoire soient alloués avant le début de l'enregistrement. Le cas échéant, ces objets apparaissent donc en premier, simplement répertoriés par nom de classe.

Par défaut, la liste est triée en fonction de la colonne Taille conservée. Pour trier selon les valeurs d'une autre colonne, cliquez sur l'en-tête de la colonne.

Cliquez sur le nom d'une classe pour ouvrir la fenêtre Vue des instances à droite (voir figure 6). Chaque instance répertoriée inclut les éléments suivants :

  • Profondeur : le plus petit nombre de sauts entre une racine GC et l'instance sélectionnée.
  • Volume de mémoire native : la mémoire native utilisée par cette instance. Cette colonne n'est visible que sur Android 7.0 et les versions ultérieures.
  • Taille superficielle : la mémoire Java utilisée par cette instance.
  • Taille conservée : l'espace mémoire dominé par cette instance (conformément aux relations de domination dans les arbres).

Figure 6 : La durée requise pour enregistrer une empreinte de la mémoire est indiquée dans la chronologie.

Pour inspecter votre tas de mémoire, procédez comme suit :

  1. Parcourez la liste pour trouver les objets ayant un nombre de tas de mémoire inhabituellement élevé susceptibles d'indiquer une fuite. Pour trouver des classes connues, cliquez sur l'en-tête de la colonne Nom de la classe pour la trier par ordre alphabétique. Ensuite, cliquez sur le nom d'une classe. Le volet Vue des instances apparaît à droite et affiche chaque instance de cette classe, comme illustré dans la figure 6.
    • Vous pouvez également localiser rapidement les objets en cliquant sur Filtrer  ou en appuyant sur Ctrl+F (Commande+F sur Mac), puis en saisissant un nom de classe ou de package dans le champ de recherche. Vous pouvez également effectuer une recherche par nom de méthode si vous sélectionnez Organiser par pile d'appels dans le menu déroulant. Si vous souhaitez utiliser des expressions régulières, cochez la case Expression régulière. Si votre recherche est sensible à la casse, cochez la case à côté de Respecter la casse.
  2. Dans le volet Vue des instances, cliquez sur une instance. L'onglet Références s'affiche en dessous, indiquant toutes les références à cet objet.

    Vous pouvez également cliquer sur la flèche à côté du nom de l'instance pour afficher tous ses champs, puis cliquer sur un nom de champ pour afficher toutes ses références. Si vous souhaitez afficher les détails de l'instance pour un champ donné, effectuez un clic droit sur le champ et sélectionnez Accéder à l'instance.

  3. Si vous identifiez une fuite de mémoire potentielle dans l'onglet Références, effectuez un clic droit sur la référence concernée, puis sélectionnez Accéder à l'instance. Cette opération sélectionne l'instance dans l'empreinte de la mémoire et affiche les données correspondantes.

Dans votre empreinte de mémoire, recherchez les éventuelles fuites causées par l'un des éléments suivants :

  • Des références de longue durée à Activity, Context, View, Drawable ou d'autres objets susceptibles de contenir une référence aux conteneurs Activity ou Context.
  • Des classes internes non statiques, telles qu'une classe Runnable, qui peuvent contenir une instance de Activity.
  • Des caches qui maintiennent des objets plus longtemps que nécessaire.

Enregistrer une empreinte de mémoire au format HPROF

Lorsque vous capturez une empreinte de la mémoire, les données ne sont consultables dans le Profileur de mémoire que tant qu'il est en cours d'exécution. L'empreinte de mémoire est perdue lorsque vous quittez la session de profilage. Si vous souhaitez l'examiner ultérieurement, vous pouvez exporter votre empreinte dans un fichier HPROF. Dans Android 3.1 et les versions antérieures, le bouton Export capture to file (Exporter la capture dans un fichier)  se trouve du côté gauche de la barre d'outils, sous la chronologie. À partir de la version 3.2, un bouton Export Heap Dump (Exporter l'empreinte de mémoire) apparaît à droite de chaque entrée d'empreinte de la mémoire dans le volet Sessions. Dans la boîte de dialogue Exporter sous forme de qui s'affiche, enregistrez le fichier avec l'extension .hprof.

Pour utiliser un autre analyseur HPROF, tel que jhat, vous devez convertir le fichier HPROF du format Android au format Java SE HPROF. Pour ce faire, utilisez l'outil hprof-conv fourni dans le répertoire android_sdk/platform-tools/. Exécutez la commande hprof-conv avec deux arguments : le fichier HPROF d'origine et l'emplacement où écrire le fichier HPROF converti. Par exemple :

hprof-conv heap-original.hprof heap-converted.hprof

Importer un fichier d'empreinte de la mémoire

Pour importer un fichier HPROF (.hprof), cliquez sur Démarrer une nouvelle session de profilage  dans le volet Sessions, sélectionnez Charger à partir du fichier, puis choisissez un fichier dans l'explorateur.

Vous pouvez également importer un fichier HPROF en le faisant glisser de l'explorateur de fichiers vers une fenêtre d'éditeur.

Détecter les fuites dans le Profileur de mémoire

Lorsque vous analysez une empreinte de mémoire dans le Profileur de mémoire, vous pouvez désormais filtrer les données de profilage qu'Android Studio considère comme potentielles sources de fuites de mémoire pour les instances Activity et Fragment de votre application.

Vous pouvez filtrer les types de données suivants :

  • Instances Activity détruites, mais qui sont toujours référencées.
  • Instances Fragment qui n'ont pas de FragmentManager valide, mais qui sont toujours référencées.

Dans certaines situations, telles que les cas suivants, le filtre peut générer des faux positifs :

  • Un Fragment a été créé, mais n'a pas encore été utilisé.
  • Un Fragment est mis en cache, mais ne fait partie d'aucune FragmentTransaction.

Pour utiliser cette fonctionnalité, commencez par capturer une empreinte de la mémoire ou importer un fichier d'empreinte de la mémoire dans Android Studio. Pour afficher les fragments et les activités susceptibles de comporter une fuite de mémoire, cochez la case Fuites d'activité/de fragment dans le volet d'empreinte de mémoire du Profileur de mémoire, comme illustré dans la figure 7.

Capture d'écran du Profileur utilisé pour détecter les fuites de mémoire

Figure 7 : Filtrage d'une empreinte de la mémoire pour identifier les fuites de mémoire.

Techniques de profilage de la mémoire

Lorsque vous utilisez le Profileur de mémoire, vous devez mettre à l'épreuve le code de votre application et essayer de forcer l'apparition des fuites de mémoire. L'une des manières de procéder consiste à exécuter votre application pendant une période prolongée avant d'inspecter le tas de mémoire. Le volume accumulé par les fuites peut les hisser au sommet des allocations du tas de mémoire. Toutefois, plus le débit de fuite est faible, plus celle-ci mettra de temps à devenir visible.

Vous pouvez également déclencher une fuite de mémoire en appliquant l'une des méthodes suivantes :

  • Faites pivoter l'appareil en mode portrait, puis en mode paysage. Répétez l'opération plusieurs fois dans différents états d'activité. La rotation de l'appareil peut souvent entraîner la fuite d'un objet Activity, Context ou View, car le système recrée le Activity. Si votre application conserve une référence à l'un de ces objets ailleurs, le système ne peut pas le récupérer.
  • Basculez entre votre application et une autre application dans différents états d'activité (accédez à l'écran d'accueil, puis revenez à votre application).

Conseil : Vous pouvez également suivre les étapes ci-dessus à l'aide du framework de test monkeyrunner.