你使用Rxjava时,内存泄漏了吗?

今天有位同学问了我一个问题,话说,问我

“有遇到网络请求一半,退出Activity造成的Theard泄露吗?已在销毁时调用了un了

我去查看了下rx的源码的unsubscribe方法,定位到一个实现类,NewThreadWorker的unsubscribe方法中,源码如下:

@Override
    public void unsubscribe() {
        isUnsubscribed = true;
        executor.shutdownNow();
        deregisterExecutor(executor);
    }

    @Override
    public boolean isUnsubscribed() {
        return isUnsubscribed;
    }

这个shutdownNow()在java的注释中写的很清楚

There are no guarantees beyond best-effort attempts to stop* processing actively executing tasks.

public interface ExecutorService extends Executor {

    /**
     * Initiates an orderly shutdown in which previously submitted
     * tasks are executed, but no new tasks will be accepted.
     * Invocation has no additional effect if already shut down.
     *
     * <p>This method does not wait for previously submitted tasks to
     * complete execution.  Use {@link #awaitTermination awaitTermination}
     * to do that.
     */
    void shutdown();

    /**
     * Attempts to stop all actively executing tasks, halts the
     * processing of waiting tasks, and returns a list of the tasks
     * that were awaiting execution.
     *
     * <p>This method does not wait for actively executing tasks to
     * terminate.  Use {@link #awaitTermination awaitTermination} to
     * do that.
     *
     * <p>There are no guarantees beyond best-effort attempts to stop
     * processing actively executing tasks.  For example, typical
     * implementations will cancel via {@link Thread#interrupt}, so any
     * task that fails to respond to interrupts may never terminate.
     *
     * @return list of tasks that never commenced execution
     */
    List<Runnable> shutdownNow();

它只是尽量保证停止所有tasks,因为,如果耗时操作没有做完,finished掉activity,同时unsubscribe 掉Subscription的话,可能还有后台线程在做一些耗时任务。那么会不会造成内存泄露呢?

我觉得还是要源码来说说话吧

 /**
     * Unsubscribe from all of the subscriptions in the list, which stops the receipt of notifications on
     * the associated {@code Subscriber}.
     */
    @Override
    public void unsubscribe() {
        if (!unsubscribed) {
            List<Subscription> list;
            synchronized (this) {
                if (unsubscribed) {
                    return;
                }
                unsubscribed = true;
                list = subscriptions;
                subscriptions = null;
            }
            // we will only get here once
            unsubscribeFromAll(list);
        }
    }

    private static void unsubscribeFromAll(Collection<Subscription> subscriptions) {
        if (subscriptions == null) {
            return;
        }
        List<Throwable> es = null;
        for (Subscription s : subscriptions) {
            try {
                s.unsubscribe();
            } catch (Throwable e) {
                if (es == null) {
                    es = new ArrayList<Throwable>();
                }
                es.add(e);
            }
        }
        Exceptions.throwIfAny(es);
    }

实际上会做一些清除引用,暂停任务的操作。因此,一般来讲在activity的ondestroy中调用unsubscribe之后,是不会造成内存泄露的。但是真的是这样的吗?让我们来做做实验吧,结果说明一切。

为了能够更好的证明这一点,我还特意做了一个app demo去验证。

主要代码:

secondActivity.png
耗时任务rx封装

demo地址已经放到了github上:

内存泄露分析

启动app,首先进入的是MainActivity,然后,我们进入SecondActivity这时候,onresume执行,我们的任务也就开始了,稍微过几秒,我们退出SecondActivity,回到MainActivity,这之后,显然,SecondActivity的ondestory方法会被执行,我们可以发现日志也停止了打印。


耗时任务正在执行

如上图所示,停留在了5就不在执行了。

这时候,我们GC一下,在导出hprof文件,注意,为什么要手动GC一下呢?因为android虚拟机去GC也是有策略的,有GC周期的。这时候可能并没有GC过,也就是说,SecondActivity的内存可能并没有被释放,但并不等于说,SecondActivity就泄露了,因为他也有可能是可以被GC的,只是还没有来得及被GC而已。总之,在导出hprof文件之前,最好先手动GC一下。就是下图那个车子,嗯,点一下吧,放心点。

GC.png

然后熟悉查看hprof文件的同学这时候可能就看到了,执行分析之后,并没有看到内存泄露。

检查内存泄漏神器.png

从上图我们看到SecondeActivity已经是红色,标明被回收了,不存在内存泄漏。

同时,我调试跟踪了一下unsubscribe调用栈,因为是一堆抽象类及接口,又有一堆的实现类,所以,最效率的方法还是调试跟踪,这时候路径就出来了,具体怎么个调发请看我的手稿,比较粗糙。

最终发现,最后的根源就是handerremoveCallbacksAndMessages 方法被调用了。

unsubscribe

removeCallbacksAndMessages.png

因此是不存在内存泄漏的,是这样的吗??让我们来在看一个例子!

假如耗时任务本来就是一个异步任务呢?


修改一下

跑几秒钟,然后回到MainActivity,这时候你在GC一下,hprof文件导出看看,我去,泄漏了。

真的泄漏了

在看看控制台,我去,一直执行到跑完。


一直跑完了

然后等跑完了,在GC一下,在导出hprof文件看看,内存泄漏依然还在,SecondActivity永久放入了你的内存,知道APP进程死掉才会释放。

不信的话,可以多启动SecondActivity,退出SecondActivity几次 ,你会发现内存不断飙升~~

内存不断飙升

我继续在思考,是不是这个耗时任务本身就丢在一个线程中执行,所以,如果我们rx不切换线程,是不是就不会泄露呢?

所以,还是不服,在改改代码,继续~

rx不切换线程了,反正是在非主线程做耗时操作

结果,并没有什么卵用,和上述情况一致,多次启动、关闭SecondActivity ,你会发现内存一样会飙升,GC后,导出hprof文件看看,一样泄露了了。

所以,大家应该懂了使用 rx的正确知识,自己的任务都同步写,线程切换交给Rx,因为Rx更懂你~~。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容