我一直在研究在创建视图时防止上下文/活动内存泄漏的最佳实践,而且我似乎无法找到关于类中静态字段允许或不允许的内容的明确答案。
假设我有这种形式的代码:
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首次膨胀时发生),静态变量似乎应该总是存在,然后保持在那里直到应用程序本身终止。如果是这种情况,是什么阻止Bitmaps和Canvas对象保持打开并填满堆?
我曾经反复重复的唯一声明就是你不能像我在构造函数中所展示的那样泄漏静态上下文,但它永远不会超越它。这真的是你唯一不能做的事情吗?
答案 0 :(得分:35)
在Java / Android中,static
变量或常量不会被垃圾回收。只要拥有它的类通过类加载器加载,它就会停留在那里。对于应用程序内的所有类,以及对所有类具有静态引用的类(例如MyInnerClass.class
),类加载器的afaik始终相同。由于类加载器没有消失,所以你的类不会这样做,因为它们被引用&amp;因此不能收集垃圾。
就像你的例子一样
public class SomeClass extends SurfaceView {
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
这确实很糟糕。即使不存在对SomeClass
的引用(例如显示自定义Activity
的{{1}}已结束),也会引用SurfaceView
(以及任何其他Context
的静态引用static
中的变量/常量将保留。您可以考虑所有这些泄漏,因为无法对SomeClass
等进行垃圾收集。如果您有常规变量引用,那么一旦包含该变量的实例变量没有更多的引用,整个实例包括对其他东西的引用可以并且将被垃圾收集.Java甚至可以处理循环引用。
对于你想要发生的常数,它通常并不坏,因为常量和它们占用的内存量并不大。常量也不(不应该)引用占用大量内存的其他实例,如Context
或Context
。
除了通过静态变量创建内存泄漏的可能性之外,如果您不希望同时对所有实例只有一个东西,也可能会产生问题。例如,如果您将Bitmap
的{{1}}保存在Bitmap
变量中,则无法拥有两个不同的图像。即使两个SurfaceView
未同时显示,您也可能会遇到问题,因为每个新实例都可能会覆盖旧图像,如果您回到另一个static
,您会意外地显示错误图片。我几乎肯定你不想在这里使用SurfaceView
。
你的内部类是SurfaceView
的事实并不意味着你必须使用静态变量 - 它只是意味着它的行为更像static
方法,因为它不能使用实例你班级中的变量(不是static class
)。
为避免内存泄漏,您根本不应该使用静态变量。除非你做特殊的事情(例如计算一个类的实例),否则不需要使用它们。常数很好。
答案 1 :(得分:0)
本文讨论可变静态字段:http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx。基本上,避免它们并改为使用常量。