web-dev-qa-db-fra.com

Android: Champs statiques et fuites de mémoire

J'ai étudié les meilleures pratiques pour éviter les fuites de mémoire de contexte/activité lors de la création de vues, et je n'arrive pas à trouver de réponse définitive à ce qui est autorisé ou non par rapport aux champs statiques dans les classes.

Disons que j'ai un code de cette forme:

public class MyOuterClass extends Activity{
   private MyInnerClass;
   MyInnerClass = (MyInnerClass) findViewById(<XML call here>);
   MyInnerClass.myXInt = 3;

   // onCreate(), onResume(), etc.

   public static class MyInnerClass extends SurfaceView implements Runnable{
      // Safe variables?
      private static int myXInt, myYInt;
      private static boolean myBoolean;
      // Potentially safe?
      private static Canvas myCanvas;
      // Definitely bad.
      private static Context myContext;

      public MyInnerClass(Context context){
         myContext = context;        // This is bad.
      }
   }
}

Je suis un peu confus sur ce que la machine virtuelle Java considère réellement comme le ClassLoader pour MyInnerClass. Techniquement, puisqu'il s'agit d'un objet SurfaceView, il semble que les variables statiques devraient toujours exister une fois que l'application a instancié MyInnerClass une fois (ce qui se produit lorsque la vue est gonflée pour la première fois), puis y rester jusqu'à ce que l'application elle-même se termine. Si tel est le cas, qu'est-ce qui empêche les objets Bitmaps et Canvas de rester ouverts et de remplir le tas?

La seule affirmation que je vois se répéter encore et encore est qu'il est impossible de filtrer le contexte statique comme je l'ai montré dans le constructeur, mais cela ne va jamais au-delà. Est-ce vraiment la seule chose que vous ne pouvez pas faire?

21
SeaNick

En Java/Android, une variable ou une constante static ne sera pas récupérée. Il y reste simplement une fois que la classe qui la détient est chargée via un chargeur de classes. Le chargeur de classe est toujours le même pour toutes les classes de votre application et c'est celui qui contient des références statiques à toutes vos classes (par exemple, MyInnerClass.class). Puisque le chargeur de classes ne s'en va pas, vos classes ne le feront pas non plus, car elles sont référencées et ne peuvent donc pas être récupérées.

Comme dans votre exemple

public class SomeClass extends SurfaceView {
  private static Context myContext;

  public MyInnerClass(Context context){
     myContext = context;        // This is bad.
  }
}

C'est vraiment mauvais. Même si aucune référence à SomeClass n'existe (par exemple, la Activity qui a montré que votre SurfaceView est terminée), la référence statique à la Context (et toute autre variable static/constante dans SomeClass restera. Vous pouvez toutes les considérer comme fuites car ce n'est pas possible. garbage collect this Context etc. Si vous avez une variable régulière référencée, alors une fois que l'instance contenant cette variable n'a plus de référence, l'instance entière, y compris ses références à d'autres éléments, peut et sera collectée. Java peut même gérer les données circulaires. références bien.

Pour les constantes, vous voulez que cela se produise et ce n’est généralement pas mauvais, car la quantité de constantes et la quantité de mémoire qu’elles occupent ne sont pas grandes. De plus, les constantes ne font pas (ne devraient pas) faire référence à d'autres instances qui utilisent de grandes quantités de mémoire, comme Context ou Bitmap.

Outre la possibilité de créer des fuites de mémoire via des variables statiques, vous pouvez également créer des problèmes si vous ne voulez pas avoir une seule chose pour toutes les instances en même temps. Par exemple, si vous enregistrez la Bitmap de votre SurfaceView dans une variable static, vous ne pouvez pas avoir deux images différentes. Même si les deux variables SurfaceViews ne sont pas affichées en même temps, des problèmes risquent de se produire, car chaque nouvelle instance écrasera probablement l'ancienne image. Si vous revenez à l'autre SurfaceView, vous affichez de manière inattendue la mauvaise image. Je suis presque sûr que vous ne voulez pas utiliser static ici.

Le fait que votre classe interne soit un static class ne signifie pas que vous devez utiliser des variables statiques - cela signifie simplement qu'elle se comporte davantage comme une méthode static car elle ne peut pas utiliser les variables d'instance (celles qui ne sont pas static) classe.

Pour éviter les fuites de mémoire, vous ne devez absolument pas utiliser de variables statiques. Il n’est pas nécessaire de les utiliser à moins d’effectuer des tâches spéciales (par exemple, compter des instances d’une classe). Les constantes vont bien.

32
zapl

Cet article parle de champs statiques mutables: http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx . En gros, évitez-les et utilisez plutôt des constantes.

0
IgorGanapolsky