web-dev-qa-db-ja.com

Android:静的フィールドとメモリリーク

私はビューの作成時にコンテキスト/アクティビティのメモリリークを防止するためのベストプラクティスを研究してきましたが、クラスの静的フィールドに関して何が許可されているか、何が許可されていないかについて明確な答えを見つけることができません。

私がこの形式のコードを持っているとしましょう:

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.
      }
   }
}

JVMがMyInnerClassのClassLoaderを実際に考慮しているものについて、少し混乱しています。技術的には、これはSurfaceViewオブジェクトであるため、アプリケーションがMyInnerClassを1回インスタンス化すると(ビューが最初に拡張されたときに発生します)、静的変数は常に存在し、アプリケーション自体が終了するまでそこにとどまるようです。その場合、ビットマップとCanvasオブジェクトが開いたままになり、ヒープがいっぱいになるのを防ぐにはどうすればよいですか?

私がこれまで何度も繰り返し見た唯一のステートメントは、コンストラクターで示したように静的コンテキストをリークできないことですが、それを超えることはありません。それが本当にできない唯一のことですか?

25
SeaNick

Java/Androidでは、static変数または定数はガベージコレクションされません。それを保持するクラスがクラスローダーを介してロードされると、そこにとどまります。クラスローダーは、アプリ内のすべてのクラスと、すべてのクラスへの静的参照(MyInnerClass.classなど)を持つクラスローダーと常に同じです。クラスローダーは消えないので、クラスは参照されるためガベージコレクトできないため、クラスもそれを行いません。

あなたの例のように

public class SomeClass extends SurfaceView {
  private static Context myContext;

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

それは確かに悪いです。 SomeClassへの参照が存在しない場合でも(たとえば、カスタムActivityが終了したことを示すSurfaceViewが終了した場合)、Context(およびその他のstatic変数/ SomeClassの定数は残ります。ガベージコレクションでContextなどできないため、これらすべてがリークしていると見なすことができます。通常の変数参照がある場合次に、その変数を含むインスタンスが変数への参照を持たなくなると、他のものへの参照を含むインスタンス全体がガベージコレクションされ、ガベージコレクションされます。Javaは循環参照もうまく処理できます。

定数の場合、それを実現したいのですが、定数の量とそれらが占有するメモリの量は大きくないので、それは通常悪くありません。また、定数はContextBitmapのような大量のメモリを消費する他のインスタンスを参照しません(すべきではありません)。

静的変数を介してメモリリークを作成する可能性に加えて、同時にすべてのインスタンスに対して単一のものだけを使用したくない場合にも問題が発生する可能性があります。たとえば、BitmapSurfaceViewstatic変数に保存する場合、2つの異なるイメージを持つことはできません。 2つのSurfaceViewsが同時に表示されない場合でも、新しいインスタンスごとに古いイメージが上書きされる可能性があり、他のSurfaceViewに戻ると、予期せずに問題が発生する可能性があります。間違った画像。ここでstaticを使用したくないと私はほぼ確信しています。

内部クラスがstatic classであるという事実は、静的変数を使用する必要があることを意味しません。インスタンス変数を使用できないため、staticメソッドのように動作することを意味します(クラスでstatic)でないもの。

メモリリークを回避するには、静的変数をまったく使用しないでください。特別なこと(クラスのインスタンスのカウントなど)を行わない限り、それらを使用する必要はありません。定数は問題ありません。

39
zapl

この記事では、可変静的フィールドについて説明します: http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx 。基本的に、それらを避け、代わりに定数を使用します。

0
IgorGanapolsky