Quels outils et méthodes Android fonctionnent le mieux pour détecter les fuites de mémoire / ressources? [fermé]

152

J'ai développé une application Android, et je suis au stade du développement d'une application pour téléphone où tout semble bien fonctionner et où vous voulez déclarer la victoire et le navire, mais vous savez qu'il doit juste y avoir des fuites de mémoire et de ressources là-dedans; et il n'y a que 16 Mo de tas sur Android et il est apparemment étonnamment facile de fuir dans une application Android.

J'ai regardé autour de moi et jusqu'à présent, je n'ai pu trouver des informations que sur «hprof» et «traceview» et aucun des deux n'a reçu beaucoup de critiques favorables.

Quels outils ou méthodes avez-vous rencontrés ou développés et souhaitez-vous partager peut-être dans un projet OS?

jottos
la source
3
Puis-je voter pour que Р̀СТȢѸ́ФХѾЦЧШЩЪЫЬѢѤЮѦѪѨѬѠѺѮѰѲѴ change son nom en quelque chose de lisible par l'homme.
JPM
1
serait-il correct de rouvrir cette question si nous supprimons le mot "Android Tools" du titre de la question? Les réponses trouvées ici étaient assez utiles pour résoudre mes problèmes de fuite de mémoire
k3b
D'accord, j'ai donc un utilisateur qui passe constamment d'une activité à l'autre, ce qui signifie qu'il peut avoir changé 15 activités en 20 secondes. Cela pourrait-il être la cause d'une erreur de mémoire insuffisante? Que dois-je faire pour y remédier? Merci!
Ruchir Baronia
1
Je ne peux pas fournir cela comme réponse car la question a été fermée, mais je vous recommande de jeter un œil à Leak Canary . Utilisez simplement votre application, ouvrez et fermez des activités et laissez la bibliothèque faire son travail. Il vous indiquera même où la fuite s'est produite. Donnez simplement à l'analyseur de fuites le temps de faire son travail après la fuite - cela prend généralement environ 2 minutes ou plus jusqu'à ce que la source de la fuite soit trouvée. Après cela, il vous le présentera parfaitement dans l'application. Aucun outil supplémentaire nécessaire!
ubuntudroid

Réponses:

90

L'une des erreurs les plus courantes que j'ai trouvées lors du développement d'applications Android est l'erreur «java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget». J'ai trouvé cette erreur fréquemment sur les activités utilisant beaucoup de bitmaps après avoir changé d'orientation: l'activité est détruite, créée à nouveau et les mises en page sont «gonflées» à partir du XML consommant la mémoire VM disponible pour les bitmaps.

Les bitmaps de la disposition d'activité précédente ne sont pas correctement désalloués par le garbage collector car ils ont croisé des références à leur activité. Après de nombreuses expériences, j'ai trouvé une assez bonne solution à ce problème.

Tout d'abord, définissez l'attribut "id" sur la vue parente de votre mise en page XML:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Ensuite, sur la méthode onDestroy () de votre activité, appelez la méthode unbindDrawables () en passant une référence à la vue parent puis effectuez un System.gc ()

@Override
protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
}


private void unbindDrawables(View view) {

    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }

        ((ViewGroup) view).removeAllViews();
    }
}

Cette méthode unbindDrawables () explore l'arborescence des vues de manière récursive et:

  1. Supprime les rappels sur tous les drawables d'arrière-plan
  2. Supprime les enfants sur chaque groupe de vues
hp.android
la source
3
Bonne solution à un problème commun.
While-E
9
Cela ne fonctionne pas pour les sous-classes d'AdapterView (ListView, GridView, etc.).
Arjun
@Arjun Oui .. cela ne fonctionne pas pour les sous-classes AdapterView. Pour cela, vous devez gérer l'exception. Reste cela fonctionne bien. C'est ce que j'utilise dans mon code et cela fonctionne très bien. J'espère que cela t'aides.
hp.android
@ hp.android Par "gérer ceci dans l'exception", avez-vous un exemple de ce à quoi cela ressemblerait? Je demande parce que j'ai des pages d'images dans mon PageAdapteret je me fais travailler par cette erreur :(
Jacksonkr
4
@Jackson modifie simplement la condition: si (voir l'instance de ViewGroup &&! (Voir l'instance de AdapterView)) cela éliminera l'exception que vous obtenez pour Adapter
hp.android
28

Principalement pour les voyageurs Google du futur:

La plupart des outils java ne sont malheureusement pas adaptés à cette tâche, car ils n'analysent que le JVM-Heap. Chaque application Android dispose également d'un tas natif, qui doit également tenir dans la limite d'environ 16 Mo. Il est généralement utilisé pour les données bitmap, par exemple. Ainsi, vous pouvez vous heurter assez facilement à des erreurs de mémoire insuffisante même si votre JVM-Heap est d'environ 3 Mo, si vous utilisez beaucoup de drawables.

Timo Ohr
la source
6
à partir d'Android 3.0 (nid d'abeille), les drawables sont stockés dans le tas
Gu1234
@Timo alors qu'utiliseriez-vous pour détecter les fuites dans le tas natif?
sydd
1
Tests, beaucoup de tests. Le problème est que vous ne pouvez même pas vraiment dire combien de mémoire votre application utilise, en raison du partage de mémoire et d'autres techniques d'optimisation. Vous pouvez obtenir des lectures de mémoire en utilisant les commandes shell habituelles, mais ce sont des estimations très, très approximatives.
Timo Ohr
20

La réponse de @ hp.android fonctionne bien si vous ne travaillez qu'avec des arrière-plans bitmap mais, dans mon cas, j'avais BaseAdapterun ensemble de ImageViews pour un GridView. J'ai modifié la unbindDrawables()méthode comme conseillé pour que la condition soit:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

mais le problème est alors que la méthode récursive ne traite jamais les enfants du AdapterView. Pour résoudre ce problème, j'ai plutôt fait ce qui suit:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

afin que les enfants de AdapterViewsoient toujours traités - la méthode n'essaye tout simplement pas de supprimer tous les enfants (ce qui n'est pas pris en charge).

Cela ne résout pas tout à fait le problème, car ils ImageViewgèrent un bitmap qui n'est pas leur arrière-plan. J'ai donc ajouté ce qui suit. Ce n'est pas idéal mais ça marche:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

Globalement, la unbindDrawables()méthode est alors:

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

J'espère qu'il existe une approche plus raisonnée pour libérer de telles ressources.

Darrenp
la source
12

Bonne discussion Google I / O (2011) sur la gestion de la mémoire sous Android, ainsi que des détails sur les outils et les techniques de profilage de la mémoire:
http://www.youtube.com/watch?v=_CruQY55HOk

greg7gkb
la source
4
Ou l'article de blog correspondant: android-developers.blogspot.com/2011/03/…
greg7gkb
1
D'accord, j'ai donc un utilisateur qui passe constamment d'une activité à l'autre, ce qui signifie qu'il peut avoir changé 15 activités en 20 secondes. Cela pourrait-il être la cause d'une erreur de mémoire insuffisante? Que dois-je faire pour y remédier? Merci!'
Ruchir Baronia
1

Eh bien, ce sont les outils qui correspondent aux formats uniques utilisés par Android ... Je pense que ce qui ne vous satisfait peut-être pas, c'est le cadre de code de test sous-jacent utilisé.

Avez-vous essayé de tester des zones de code simulées à l'aide de Android Mock Framework?

Fred Grott
la source
1
pas tellement, les tests de cette nature ne sont pas tant le problème que l'enregistrement de ce qui se passe réellement pendant l'exécution de l'application, ce dont j'ai vraiment besoin est un outil de profilage des fuites de ressources / mémoire
jottos