Android导致内存泄漏的常见情况
1、问题描述:
在Activity中注册一个Local Broadcast Receiver,当Activity关闭的时候没有反注册Local Broadcast Receiver,就会一直持有此Activity引用。
解决方案:
在Activity的onStop方法中记得调用反注册receiver。对于注册与反注册receiver的正确使用方法应该是在onStart() or onResume()方法中注册,在onStop()方法中反注册。
2、问题描述:
静态Activity或View引用: 比如声明了一个静态的Textview,此Textview直接或间接的持有Activity或View的静态引用,当Activity或View被销毁时,是不会被GC回收掉的。
解决方案:
在Activity、View、Context中一定不用静态变量。
3、问题描述:
单例类引用,比如 给一个耗时操作的单例类传递了当前上下文Context
解决方案
A、用applicationContext()代替Activity上下文。
B、如果非得用Activity上下文,一定记住在Activity被销毁的时候,确定传入单例类的Context被置为null。
4、问题描述:
内部类引用:在声明的内部类里面,为了跳转到另外一个Activity而传入当前的Activity。在class字节码层面的putfield 这一行,这里表示有一个对 NonStaticInnerDemo的引用被存在了 this$0 中,也就是说它持有了外部类的对象。
解决方案:
A、永远不要创建内部类的静态变量。
B、内部类设置为静态的。当匿名类的实例是“静态的”时,它们就不会隐式持有对其外部类的引用。
C、任何的view或Activity都使用弱引用。仅有弱引用指向他们,这样GC是可以对他们进行回收的。
java源码
public class NonStaticInnerDemo {
private static String TAG = "Outter";
private Runnable runnable = new Runnable(){
public void run(){
System.out.println("inner run: " + TAG);
}
};
}
class字节码
Compiled from "NonStaticInnerDemo.java"
class NonStaticInnerDemo$1 implements java.lang.Runnable {
final NonStaticInnerDemo this$0;
NonStaticInnerDemo$1(NonStaticInnerDemo);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LNonStaticInnerDemo;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: return
public void run();
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #4 // class java/lang/StringBuilder
6: dup
7: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
10: ldc #6 // String inner run:
12: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokestatic #8 // Method NonStaticInnerDemo.access$000:()Ljava/lang/String;
18: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: return
}
private static Runnable runnable = new Runnable(){}
Compiled from "NonStaticInnerDemo.java"
final class NonStaticInnerDemo$1 implements java.lang.Runnable {
NonStaticInnerDemo$1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void run();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String inner run:
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokestatic #7 // Method NonStaticInnerDemo.access$000:()Ljava/lang/String;
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: return
}
对比可以发现这里少了一行 putfield,class字节码第九行。说明对于一个静态匿名内部类来说,它不会持有外部类的引用。
5、问题描述:
匿名类引用,比如在Activity内,声明出一个Handler处理Message,Message没有处理完,Activity却执行了销毁动作,Message持有Handler,Handler持有Activity,导致Activity始终不能被回收。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// do something
}
};
解决方案:
A、在Activity 的 onDestroy方法中执行 Handler的removeCallbacksAndMessages(null)方法, 将所有Message移除。
B、内部类设置为静态的,不隐性持有外部类,如果静态内部类需要用到外面的类,那么就使用弱引用。
C、在Handler 内部使用Activity的弱引用。Handler在主线程上处理消息时,它与Looper的消息队列相关联的。
6、问题描述:
异步任务引用,用AsyncTask的OnPostExecute()方法,把字符串更新到Textview上。
解决方案:
A、避免在在Activity被销毁的时候AsyncTask还在一直处于执行状态,我们此时应该取消掉AsyncTask。
B、对Textview使用弱引用。
7、问题描述:
线程Thread引用
解决方案:
非静态匿名类持有对其外部类的隐式引用。在Activity的onDestroy()方法中关闭线程。
8、问题描述:
定时器任务 TimerTask 引用
解决方案:
对于TimerTask,也可以遵循与线程相同的原则。在Activity的onDestroy()方法中取消定时器,以避免内存泄漏。
9、问题描述:
数据链接没有关闭,比如数据库contentprovider,io,sokect、cursor
数据库contentprovider,io,sokect、cursor
解决方案:
使用结束后,及时释放关闭
总结
虽然这些都是常见的,但不得不引起每个人的注意。最近总想写点东西,总结过去不失为一种反思。
吾每日三省吾身 –《论语·学而》
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!