欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android内存泄露:handler

程序员文章站 2022-07-14 18:30:00
...

一、什么是内存泄露

Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。但是有时候我们使用内存对象在需要销毁的时候这个对象因为被其他对象引用而无法销毁,那么这时候这个对象就属于内存泄露了。

上面说的一段,说的比较官方,有一些需要解释一下

1 GC

GC 是 garbage collection 的缩写, 垃圾回收的意思. 也可以是 Garbage Collector, 也就是垃圾回收器.

GC有三大职责:

  1. 分配内存;
  2. 确保任何被引用的对象保留在内存中;
  3. 回收不能通过引用关系找到的对象的内存.

2 不可到达

所有对象引用,向上可追溯到GC Roots,则是可到达;否则是不可到达。

所谓GC Root我们可以理解为是一个Heap内存之外的对象, 通常包括但不仅限于如下几种:

  1. System Class 系统Class Loader加载的类. 例如java运行环境中rt.jar中类, 比如java.util.* package中的类.
  2. Thread 运行中的线程
  3. JNI 中的本地/全局变量, 用户自定义的JNI代码或是JVM内部的.
  4. Busy Monitor 任何调用了wait()或notify()方法, 或是同步化的(synchronized)的东西. 可以理解为同步监控器.
  5. Java本地实例, 还在运行的Thread的stack中的方法创建的对象.

活对象/垃圾

如果这个对象是引用可达的, 则称之为活的(live), 反之, 如果这个对象引用不可达, 则称之为死的(dead), 也可以称之为垃圾(garbage).

这个引用可达与不可达就是相对于GC Root来说的:

二、handler的基本使用与内存泄露的原因

1 在主线程中定义handler并重写handlerMessage方法

Handler handler = new Handler() {  

        @Override  
        public void handleMessage(Message msg) {  
            dosomething();  

        }  
};  

2在线程中调用

handler.sendEmptyMessage()  

3 问题的原因

这样就实现了子线程与主线程的通信;
但是这种看似正常的写法其实有一些不可预知的错误;我们定义的handler对象其实是一个内部匿名了它会隐式的持有外部类的引用,我们可以看到

public boolean isGoToComplete = false;  

    Handler handler = new Handler() {  

        @Override  
        public void handleMessage(Message msg) {  
            if (isGoToComplete == false) {  

            }  

        }  
    };  

它默认是可以使用外部类的成员变量的,这样也佐证了我们所说的它会隐式的持有外部类的引用;

mHandler.sendMessageDelayed(Message.obtain(), 60000);

这时候如果子线程使用handler将message消息发送到messageQueue中并等待执行的过程过长,这时候activity已经执行finish方法;
那么我们希望的是activity被执行onDestory方法,然后activity相关的各种资源,组件都被销毁掉,但是由于handler隐式的持有activity的引用,那么activity就无法被回收,activity相关的资源与组件也无法被回收
–即内存已经泄露。

三、解决方法

1 使用静态变量定义handler

static Handler handler = new Handler() {  

        @Override  
        public void handleMessage(Message msg) {  
            dosomething();  

        }  
    }; 

handler对象由于是静态类型无法持有外部activity的引用,但是这样Handler不再持有外部类的引用,导致程序不允许在Handler中操作activity中的对象了,这时候我们需要在Handler对象中增加一个队activity的弱引用;

static class MyHandler extends Handler {  
    WeakReference<Activity> mActivityReference;  
        MyHandler(Activity activity) {  
        mActivityReference= new WeakReference<Activity>(activity);  
    }  
    @Override  
    public void handleMessage(Message msg) {  
        final Activity activity = mActivityReference.get();  
        if (activity != null) {  
            mImageView.setImageBitmap(mBitmap);  
      }  
    }  
} 

2 当Activity finish后 handler对象还是在Message中排队。 还是会处理消息,这些处理有必要?

正常Activitiy finish后,已经没有必要对消息处理,那需要怎么做呢?
解决方案也很简单,在Activity onStop或者onDestroy的时候,取消掉该Handler对象的Message和Runnable。
通过查看Handler的API,它有几个方法:removeCallbacks(Runnable r)和removeMessages(int what)等。

@Override  
public void onDestroy() {  
    //  If null, all callbacks and messages will be removed.  
    mHandler.removeCallbacksAndMessages(null);  
}