Handler内存泄漏的原因是什么?

2022-01-21  本文已影响0人  GoLearning轻松学

Handler内存泄漏的原因是什么?

使用handler时,ide报内存泄漏警告

这里提醒我们,这个handler必须时静态的,否则有可能会产生内存泄漏,所有的内部类都会引用外部类的引用,为什么只有handler会提示内存泄漏呢?
原因之一内部类持有外部类的引用,这都知道。

42c27bddab3ceca59705a99da7a1ad5.png

因为内部类持有外部类的引用,所以这里可以直接调用到text1.setText("xxx");

要说清除handler内存泄漏的原因,这里就要提到JVM的垃圾回收机制中GCRoot的概念,有个可达性分析,标记的过程就是基于可达性分析来进行的,什么时可达性分析?就是一个持有链,GCRoot持有的就是可达的,或者时被GCRoot简介持有的,都是可达的,一旦可达,意味这它就被这个GCRoot持有了,被GCRoot持有的对象都是不能够进行垃圾回收的

handler的持有链
public class Handler {
.......
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
......
}
public final class Message implements Parcelable {
........
    @UnsupportedAppUsage
    /*package*/ Handler target;

    @UnsupportedAppUsage
    /*package*/ Runnable callback;

    // sometimes we store linked lists of these things
    @UnsupportedAppUsage
    /*package*/ Message next;
......

首先匿名内部类持有了外部类的链条,handler持有了Activity,handler->Activity,那handler被谁持有了,看上面的代码,在enqueueMessage的时候msg.target = this;再看Message的类,变量target,就是Handler,所以这里链条就变成了Message->Handler->Activity,那message被谁持有,我们的message入队列的时候,丢给了messageQueue,所以messageQueue持有了message。messageQueue->Message->handler->Activity。

public final class Looper {
.....
final MessageQueue mQueue
  public static void loop() {
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
           
        }
}

Looper的loop()操作,会对MessageQueue进行轮询操作,所以是Looper持有了MessageQueue,Looper->messageQueue->Message->handler->Activity。然后就是ActivityThread持有了Looper,ActivityThread->Looper->messageQueue->Message->handler->Activity。

分析:
ActivityThread不会释放Looper,Looper持有的MessageQueue,不会释放,message呢?message会释放吗?

    public static void loop() {
......
          msg.target.dispatchMessage(msg);
 ......
          msg.recycleUnchecked();
     }

在message处理完的时候会被释放,会调用msg.recycleUnchecked();去释放。如果message做了一个定时器,2分钟之后才执行,这个message不会被释放,如果是20分钟,那就意味着这个message会在messageQueue里面至少20分钟,这因为message会持有handler,持有activity,这个时候就是内存泄漏,按道理来说,activity走onDestroy(),就会立即释放,但是由于message间接持有了activity,所以它不会释放,handler导致activity内存泄漏的原因就是因为这个持有链条的存在。
最简单的解决办法就是用static,handle创建的是静态的,就没有问题,不持有外部对象,它是全局的。

上一篇下一篇

猜你喜欢

热点阅读