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

解决方案:

使用结束后,及时释放关闭

总结

虽然这些都是常见的,但不得不引起每个人的注意。最近总想写点东西,总结过去不失为一种反思。

吾每日三省吾身 –《论语·学而》


Android

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

 目录