Android:静态字段和内存泄漏
Posted
技术标签:
【中文标题】Android:静态字段和内存泄漏【英文标题】:Android : Static Fields and Memory Leaks 【发布时间】:2012-08-08 03:10:07 【问题描述】:我一直在研究在创建视图时防止上下文/活动内存泄漏的最佳实践,但对于类中的静态字段,我似乎无法找到明确的答案。
假设我有一个这种形式的代码:
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 一次(这发生在 View 第一次膨胀时),静态变量似乎应该始终存在,然后保持在那里直到应用程序本身终止。如果是这样,是什么阻止了位图和画布对象也保持打开状态并填满堆?
我见过的唯一一个反复重复的声明是,你不能像我在构造函数中显示的那样泄漏静态上下文,但它永远不会超出这个范围。这真的是你唯一不能做的事吗?
【问题讨论】:
您的Canvas
等不必是static
。这样它确实会永远留在堆中
如果是这种情况,那么是什么阻止了常量(即 - private static final int MY_CONSTANT)也保持任何扩展活动(及其上下文)打开的类?
【参考方案1】:
在 Java/android 中,static
变量或常量不会被垃圾回收。一旦持有它的类通过类加载器加载,它就会停留在那里。对于您的应用程序中的所有类,类加载器始终是相同的,并且它具有对您所有类的静态引用(例如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
中的任何其他static
变量/常量)仍将保留。您可以认为所有这些都已泄漏,因为不可能对 Context
等进行垃圾收集。如果您有一个常规变量引用某些东西,那么一旦包含该变量的实例不再引用它,整个实例包括它对它的引用其他东西可以并且将会被垃圾回收。Java 甚至可以很好地处理循环引用。
对于您希望发生的常量,这通常还不错,因为常量的数量和它们占用的内存量并不大。常量也不(不应该)引用其他占用大量内存的实例,例如 Context
或 Bitmap
。
除了通过静态变量创建内存泄漏的可能性之外,如果您不想同时为所有实例提供一个单一的东西,您也可能会产生问题。例如,如果您将SurfaceView
的Bitmap
保存在static
变量中,则不能有两个不同的图像。即使两个SurfaceView
s 没有同时显示,您也可能会遇到问题,因为每个新实例可能会覆盖旧图像,如果您返回另一个SurfaceView
,您会意外显示错误的图像。我几乎可以肯定你不想在这里使用static
。
您的内部类是 static class
的事实并不意味着您必须使用静态变量 - 它只是意味着它的行为更像 static
方法,因为它不能使用实例变量(那些不是static
)。
为避免内存泄漏,您根本不应该使用静态变量。除非您做特殊的事情(例如计算类的实例),否则无需使用它们。常量很好。
【讨论】:
谢谢。我猜想要静态的总体原因是因为从概念上讲,大多数 View 对象只需要一个实例,因为它们通常代表特定的屏幕或屏幕上的对象。使它们成为静态的代码似乎更有效,但我理解那里的缺点。让变量完全受 GC 支配的唯一困扰我的是,我无法控制何时需要释放内存,而且从我在应用程序中看到的所有内容来看,它总是在位图操作期间,恐怕会变成抖动的帧速率。 你不需要有一些静态的东西来保持它的活力和重用它。只要您有对它的引用,它就会存在。如果您忘记适当地null
所有静态引用,使用静态通常会导致更多的内存消耗。 + 例如,如果您旋转屏幕,通常会重新创建视图实例
我保持屏幕方向锁定,但这是一个非常好的点。再次感谢!
很好的解释,我使用了对上下文的弱引用来避免内存泄漏【参考方案2】:
本文讨论可变静态字段:http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx。基本上,避免使用它们并改用常量。
【讨论】:
以上是关于Android:静态字段和内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章
Android技术分享|Android 中部分内存泄漏示例及解决方案
Android性能优化之利用LeakCanary检测内存泄漏及解决办法(转)