Android:静态字段和内存泄漏

33

我一直在研究如何在创建视图时防止Context/Activity内存泄漏的最佳做法,但似乎找不到关于类中静态字段允许或不允许使用的明确答案。

假设我有以下代码:

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 等不需要是 static 的。这样它确实会永远留在堆中。 - zapl
2
如果是这种情况,那么什么阻止常量(即 - private static final int MY_CONSTANT)也持有扩展活动(及其上下文)的任何类呢? - SeaNick
2个回答

51
在Java/Android中,静态变量或常量不会被垃圾回收。一旦包含它的类通过类加载器加载,它就会一直存在。据我所知,类加载器对于应用程序中所有类都是相同的,它具有对所有类的静态引用(例如MyInnerClass.class)。由于类加载器不会消失,因此您的类也不会被垃圾回收,因为它们被引用并且因此不能被垃圾回收。
就像你的例子中一样。
public class SomeClass extends SurfaceView {
  private static Context myContext;

  public MyInnerClass(Context context){
     myContext = context;        // This is bad.
  }
}
那确实很糟糕。即使没有引用到SomeClass(例如显示自定义SurfaceView的Activity已经结束),对Context(以及SomeClass中其他静态变量/常量)的静态引用仍然存在,您可以认为它们都泄漏了,因为无法回收那个Context等。如果常规变量引用了某些东西,那么一旦包含该变量的实例不再引用它,整个实例(包括对其他内容的引用)都可以被垃圾回收。Java甚至可以很好地处理循环引用。
对于常量,您希望这种情况发生,通常情况下也不会有问题,因为常量的数量和它们占用的内存量并不大。而且常量不应该引用像Context或Bitmap这样占用大量内存的其他实例。
除了通过静态变量创建内存泄漏的可能性之外,如果您不想同时为所有实例只使用单个对象,还可能出现问题。例如,如果将SurfaceView的Bitmap保存在静态变量中,则无法拥有两个不同的图像。即使两个SurfaceView不同时显示,您也可能遇到问题,因为每个新实例都可能覆盖旧图像,如果返回到其他SurfaceView,则意外显示错误的图像。我几乎确定您不想在此处使用静态变量。
您的内部类是静态类并不意味着必须使用静态变量,它只是表现得更像静态方法,因为它不能使用类中非静态的实例变量。
为了避免内存泄漏,您根本不应该使用静态变量。除非进行特殊操作(例如计算类的实例数),否则没有必要使用它们。常量是可以使用的。

谢谢。我想,希望使用静态变量的总体原因是因为在概念上,大多数View对象只需要一个实例,因为它们通常代表屏幕上的特定屏幕或对象。从代码角度来看,使它们成为静态变量似乎更加高效,但我理解其中的缺点。唯一让我困扰的是将变量完全交给GC控制,我无法控制它何时需要释放内存,而且从我在应用程序中看到的所有内容来看,它总是在位图操作期间进行,这可能会导致抖动的帧速率。 - SeaNick
2
你不需要将某些东西设为静态以保持其活性并重复使用。只要你有对它的引用,它就会存在。如果你忘记适当地将所有静态引用设置为“null”,使用静态通常会导致更多的内存消耗。此外,例如旋转屏幕时,视图实例通常会被重新创建。 - zapl
1
我一直保持屏幕方向锁定,但这是一个非常好的观点。再次感谢! - SeaNick
很好的解释,我使用弱引用来避免内存泄漏问题。 - suitianshi

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接