美文网首页
Handler内存泄漏的原因是什么?

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

作者: GoLearning轻松学 | 来源:发表于2022-01-21 11:08 被阅读0次

    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创建的是静态的,就没有问题,不持有外部对象,它是全局的。

    相关文章

      网友评论

          本文标题:Handler内存泄漏的原因是什么?

          本文链接:https://www.haomeiwen.com/subject/gmxehrtx.html