J'ai passé 4 jours entiers à essayer tout ce que je pouvais pour trouver la fuite de mémoire dans une application que je développais, mais les choses ont cessé de faire sens il y a longtemps.
L'application que je développe est de nature sociale, alors pensez au profil Activités (P) et répertoriez les activités avec des données - par exemple des badges (B). Vous pouvez passer d'un profil à une liste de badges, à d'autres profils, à d'autres listes, etc.
Alors imaginez un flux comme celui-ci P1 -> B1 -> P2 -> B2 -> P3 -> B3, etc. Par souci de cohérence, je charge les profils et les badges du même utilisateur, donc chaque page P est la même et donc chaque page B.
L'essentiel du problème est le suivant: après avoir navigué un peu, en fonction de la taille de chaque page, j'obtiens une exception de mémoire insuffisante à des endroits aléatoires - Bitmaps, Strings, etc. - cela ne semble pas cohérent.
Après avoir fait tout ce qui est imaginable pour comprendre pourquoi je manque de mémoire, je n'ai rien trouvé. Ce que je ne comprends pas, c'est pourquoi Android ne tue pas P1, B1, etc. s'il manque de mémoire lors du chargement et se bloque à la place. Je m'attendrais à ce que ces activités antérieures meurent et soient ressuscitées si jamais je reviens à elles via onCreate () et onRestoreInstanceState ().
Sans parler de cela - même si je fais P1 -> B1 -> Back -> B1 -> Back -> B1, je reçois toujours un crash. Cela indique une sorte de fuite de mémoire, mais même après avoir vidé hprof et utilisé MAT et JProfiler, je ne peux pas le localiser.
J'ai désactivé le chargement des images à partir du Web (et augmenté les données de test chargées pour compenser et rendre le test équitable) et me suis assuré que le cache d'images utilise SoftReferences. Android essaie en fait de libérer les quelques SoftReferences dont il dispose, mais juste avant de perdre la mémoire.
Les pages de badges obtiennent des données du Web, les chargent dans un tableau d'EntityData à partir d'un BaseAdapter et les alimentent dans un ListView (j'utilise en fait l' excellent MergeAdapter de CommonsWare , mais dans cette activité de Badge, il n'y a vraiment qu'un seul adaptateur de toute façon, mais je voulait mentionner ce fait de toute façon).
J'ai parcouru le code et je n'ai rien trouvé qui fuirait. J'ai effacé et annulé tout ce que je pouvais trouver et même System.gc () à gauche et à droite, mais l'application se bloque toujours.
Je ne comprends toujours pas pourquoi les activités inactives qui sont sur la pile ne sont pas récoltées, et j'aimerais vraiment comprendre cela.
À ce stade, je cherche des indices, des conseils, des solutions ... tout ce qui pourrait aider.
Je vous remercie.
la source
outOfMemory
erreur. Merci!Réponses:
Ce n'est pas ainsi que les choses fonctionnent. La seule gestion de la mémoire qui a un impact sur le cycle de vie de l'activité est la mémoire globale sur tous les processus, car Android décide qu'il manque de mémoire et doit donc tuer les processus d'arrière-plan pour en récupérer.
Si votre application est assise au premier plan et commence de plus en plus d'activités, elle ne passe jamais en arrière-plan, elle atteindra donc toujours sa limite de mémoire de processus locale avant que le système ne soit sur le point de tuer son processus. (Et quand il tue son processus, il tuera le processus hébergeant toutes les activités, y compris ce qui est actuellement au premier plan.)
Il me semble donc que votre problème de base est le suivant: vous laissez trop d’activités se dérouler en même temps et / ou chacune de ces activités consomme trop de ressources.
Il vous suffit de repenser votre navigation pour ne pas compter sur l'empilement d'un nombre arbitraire d'activités potentiellement lourdes. À moins que vous ne fassiez beaucoup de choses dans onStop () (comme appeler setContentView () pour effacer la hiérarchie des vues de l'activité et effacer les variables de tout ce à quoi elle pourrait s'accrocher), vous allez simplement manquer de mémoire.
Vous voudrez peut-être envisager d'utiliser les nouvelles API Fragment pour remplacer cette pile arbitraire d'activités par une seule activité qui gère plus étroitement sa mémoire. Par exemple, si vous utilisez les fonctionnalités de la pile arrière des fragments, lorsqu'un fragment va sur la pile arrière et n'est plus visible, sa méthode onDestroyView () est appelée pour supprimer complètement sa hiérarchie de vues, réduisant considérablement son encombrement.
Maintenant, dans la mesure où vous vous écrasez dans le flux où vous appuyez en arrière, allez à une activité, appuyez en arrière, passez à une autre activité, etc. et n'avez jamais de pile profonde, alors oui, vous avez juste une fuite. Cet article de blog décrit comment déboguer les fuites: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html
la source
Activity
JavaDocs.Quelques conseils:
Assurez-vous que vous n'êtes pas une fuite de contexte d'activité.
Assurez-vous de ne pas conserver de références sur les bitmaps. Nettoyez tous vos ImageView dans Activity # onStop, quelque chose comme ceci:
Recyclez les bitmaps si vous n'en avez plus besoin.
Si vous utilisez un cache mémoire, comme memory-lru, assurez-vous qu'il n'utilise pas trop de mémoire.
Non seulement les images prennent beaucoup de mémoire, mais assurez-vous de ne pas conserver trop d'autres données en mémoire. Cela peut facilement se produire si vous avez des listes infinies dans votre application. Essayez de mettre en cache les données dans DataBase.
Sur Android 4.2, il y a un bogue (stackoverflow # 13754876) avec l'accélération matérielle, donc si vous utilisez
hardwareAccelerated=true
dans votre manifeste, il y aura une fuite de mémoire.GLES20DisplayList
- gardez les références, même si vous avez fait l'étape (2) et que personne d'autre ne fait référence à ce bitmap. Ici vous avez besoin de:a) désactiver l'accélération matérielle pour l'API 16/17;
ou
b) détacher la vue contenant le bitmap
Pour Android 3+, vous pouvez essayer d'utiliser
android:largeHeap="true"
dans votreAndroidManifest
. Mais cela ne résoudra pas vos problèmes de mémoire, il suffit de les reporter.Si vous avez besoin, comme, d'une navigation infinie, alors Fragments - devrait être votre choix. Vous aurez donc 1 activité, qui basculera simplement entre les fragments. De cette façon, vous résoudrez également certains problèmes de mémoire, comme le numéro 4.
Utilisez Memory Analyzer pour trouver la cause de votre fuite de mémoire.
Voici une très bonne vidéo de Google I / O 2011: Gestion de la mémoire pour les applications Android
Si vous traitant de bitmaps cela devrait être un incontournable: Affichage bitmaps Efficacement
la source
outOfMemory
erreur. Merci!Les bitmaps sont souvent à l'origine d'erreurs de mémoire sur Android, ce serait donc un bon domaine à vérifier.
la source
outOfMemory
erreur. Merci!Avez-vous des références à chaque activité? AFAIK c'est une raison qui empêche Android de supprimer des activités de la pile.
Êtes-vous en mesure de reproduire cette erreur sur d'autres appareils également? J'ai rencontré un comportement étrange de certains appareils Android en fonction de la ROM et / ou du fabricant du matériel.
la source
Je pense que le problème est peut-être une combinaison de nombreux facteurs énoncés ici dans les réponses qui vous posent des problèmes. Comme @Tim l'a dit, une référence (statique) à une activité ou à un élément de cette activité peut amener le GC à ignorer l'activité. Voici l'article traitant de cette facette. Je pense que le problème probable vient de quelque chose qui maintient l'activité dans un état "Processus visible" ou plus, ce qui garantira à peu près que l'activité et ses ressources associées ne seront jamais récupérées.
J'ai traversé le problème inverse il y a quelque temps avec un service, c'est donc ce qui m'a poussé à réfléchir: il y a quelque chose qui maintient votre activité en haut de la liste des priorités de processus afin qu'elle ne soit pas soumise au système GC, comme une référence (@Tim) ou une boucle (@Alvaro). La boucle n'a pas besoin d'être un élément sans fin ou long, juste quelque chose qui fonctionne beaucoup comme une méthode récursive ou une boucle en cascade (ou quelque chose du genre).
EDIT: Si je comprends bien, onPause et onStop sont appelés automatiquement selon les besoins par Android. Les méthodes sont là principalement pour que vous puissiez les remplacer afin que vous puissiez prendre soin de ce dont vous avez besoin avant que le processus d'hébergement ne soit arrêté (sauvegarde des variables, sauvegarde manuelle de l'état, etc.); mais notez qu'il est clairement indiqué que onStop (avec onDestroy) peut ne pas être appelé dans tous les cas. De plus, si le processus d'hébergement héberge également une activité, un service, etc. qui a un statut «Forground» ou «Visible», le système d'exploitation peut même ne pas envisager d'arrêter le processus / thread. Par exemple: une activité et un service sont tous deux ancrés dans le même processus et le service revient
START_STICKY
deonStartCommand()
le processus prend automatiquement au moins un statut visible. Cela pourrait être la clé ici, essayez de déclarer un nouveau proc pour l'activité et voyez si cela change quelque chose. Essayez d'ajouter cette ligne à la déclaration de votre activité dans le manifeste en tant que:android:process=":proc2"
puis réexécutez les tests si votre activité partage un processus avec autre chose. L'idée ici est que si vous avez nettoyé votre activité et êtes à peu près sûr que le problème n'est pas votre activité, alors quelque chose d'autre est le problème et il est temps de chercher cela.De plus, je ne me souviens pas où je l'ai vu (si je l'ai même vu dans la documentation Android), mais je me souviens que quelque chose à propos du
PendingIntent
référencement d'une activité peut entraîner le comportement d'une activité de cette façon.Voici un lien vers la
onStartCommand()
page avec quelques informations sur le processus de non-meurtre.la source
donc la seule chose à laquelle je peux vraiment penser est si vous avez une variable statique qui fait référence directement ou indirectement au contexte. Même quelque chose comme une référence à une partie de l'application. Je suis sûr que vous l'avez déjà essayé mais je le suggérerai juste au cas où, essayez simplement d'annuler TOUTES vos variables statiques dans onDestroy () juste pour vous assurer que le garbage collector l'obtient
la source
La plus grande source de fuite de mémoire que j'ai trouvée était causée par une référence globale, de haut niveau ou de longue date au contexte. Si vous conservez le «contexte» stocké dans une variable n'importe où, vous pouvez rencontrer des fuites de mémoire imprévisibles.
la source
Essayez de passer getApplicationContext () à tout ce qui nécessite un contexte. Vous pouvez avoir une variable globale qui contient une référence à vos activités et les empêche d'être garbage collection.
la source
L'une des choses qui a vraiment aidé le problème de mémoire dans mon cas a fini par être de définir inPurgeable sur true pour mes Bitmaps. Voir Pourquoi est-ce que je n'utiliserais jamais l'option inPurgeable de BitmapFactory? et la discussion de la réponse pour plus d'informations.
La réponse de Dianne Hackborn et notre discussion ultérieure (également merci, CommonsWare) ont aidé à clarifier certaines choses sur lesquelles j'étais confuse, alors merci pour cela.
la source
J'ai rencontré le même problème avec vous. Je travaillais sur une application de messagerie instantanée, pour le même contact, il est possible de démarrer un ProfileActivity dans un ChatActivity, et vice versa. J'ajoute simplement une chaîne supplémentaire dans l'intention de démarrer une autre activité, elle prend les informations du type de classe d'activité de démarrage et l'ID utilisateur. Par exemple, ProfileActivity démarre un ChatActivity, puis dans ChatActivity.onCreate, je marque le type de classe invocateur 'ProfileActivity' et l'ID utilisateur, s'il va démarrer une activité, je vérifierais s'il s'agit d'un 'ProfileActivity' pour l'utilisateur ou non . Si c'est le cas, appelez simplement 'finish ()' et revenez à l'ancien ProfileActivity au lieu d'en créer un nouveau. La fuite de mémoire est une autre chose.
la source