你使用Rxjava时,内存泄漏了吗?
来源:互联网 发布:淘宝卖家中心登录网 编辑:程序博客网 时间:2024/06/18 03:23
今天有位同学问了我一个问题,话说,问我
“有遇到网络请求一半,退出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去验证。
主要代码:
![](http://upload-images.jianshu.io/upload_images/1019822-120b9cfe1ba34cd5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/1019822-0771b2f594a28850.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
demo地址已经放到了github上:
内存泄露分析
启动app,首先进入的是MainActivity,然后,我们进入SecondActivity这时候,onresume执行,我们的任务也就开始了,稍微过几秒,我们退出SecondActivity,回到MainActivity,这之后,显然,SecondActivity的ondestory方法会被执行,我们可以发现日志也停止了打印。
![](http://upload-images.jianshu.io/upload_images/1019822-8339d74ff4897bb4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如上图所示,停留在了5就不在执行了。
这时候,我们GC一下,在导出hprof
文件,注意,为什么要手动GC一下呢?因为android虚拟机去GC也是有策略的,有GC周期的。这时候可能并没有GC过,也就是说,SecondActivity的内存可能并没有被释放,但并不等于说,SecondActivity就泄露了,因为他也有可能是可以被GC的,只是还没有来得及被GC而已。总之,在导出hprof
文件之前,最好先手动GC一下。就是下图那个车子,嗯,点一下吧,放心点。
![](http://upload-images.jianshu.io/upload_images/1019822-0b7decc8fbf6984e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后熟悉查看hprof
文件的同学这时候可能就看到了,执行分析之后,并没有看到内存泄露。
![](http://upload-images.jianshu.io/upload_images/1019822-4418ecaa52af49b1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
从上图我们看到SecondeActivity已经是红色,标明被回收了,不存在内存泄漏。
同时,我调试跟踪了一下unsubscribe
调用栈,因为是一堆抽象类及接口,又有一堆的实现类,所以,最效率的方法还是调试跟踪,这时候路径就出来了,具体怎么个调发请看我的手稿,比较粗糙。
最终发现,最后的根源就是hander
的 removeCallbacksAndMessages
方法被调用了。
![](http://upload-images.jianshu.io/upload_images/1019822-c4bd7a014ec880de.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/1019822-557d07008b4b0054.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
因此是不存在内存泄漏的,是这样的吗??让我们来在看一个例子!
假如耗时任务本来就是一个异步任务呢?
![](http://upload-images.jianshu.io/upload_images/1019822-e1aa39e034e6debd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
跑几秒钟,然后回到MainActivity,这时候你在GC一下,hprof文件导出看看,我去,泄漏了。
![](http://upload-images.jianshu.io/upload_images/1019822-fdf203344088842d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在看看控制台,我去,一直执行到跑完。
![](http://upload-images.jianshu.io/upload_images/1019822-370c8a5547e56fa6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后等跑完了,在GC一下,在导出hprof文件看看,内存泄漏依然还在,SecondActivity永久放入了你的内存,知道APP进程死掉才会释放。
不信的话,可以多启动SecondActivity,退出SecondActivity几次 ,你会发现内存不断飙升~~
![](http://upload-images.jianshu.io/upload_images/1019822-ad31107f0c66cc40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我继续在思考,是不是这个耗时任务本身就丢在一个线程中执行,所以,如果我们rx不切换线程,是不是就不会泄露呢?
所以,还是不服,在改改代码,继续~
![](http://upload-images.jianshu.io/upload_images/1019822-2174a663ebc68da5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
结果,并没有什么卵用,和上述情况一致,多次启动、关闭SecondActivity ,你会发现内存一样会飙升,GC后,导出hprof
文件看看,一样泄露了了。
所以,大家应该懂了使用 rx的正确知识,自己的任务都同步写,线程切换交给Rx,因为Rx更懂你~~。
- 你使用Rxjava时,内存泄漏了吗?
- Android 使用RxLifecycle解决RxJava内存泄漏
- Android rxjava内存泄漏问题
- RxLifecycle解决RxJava内存泄漏
- 解决rxjava导致的内存泄漏
- RxJava 的 CompositeSubscription与内存泄漏
- 你不必使用弱引用以避免内存泄漏
- 可能遇到内存泄漏了
- 亲爱的,内存泄漏了
- 夭兽啦,内存泄漏了怎么办
- 你的进程使用了多少内存?
- Android性能优化之利用Rxlifecycle解决RxJava内存泄漏
- Android性能优化之利用Rxlifecycle解决RxJava内存泄漏
- Android性能优化之利用Rxlifecycle解决RxJava内存泄漏
- Android性能优化之利用Rxlifecycle解决RxJava内存泄漏
- (GeekBand或极客班) 你的内存泄漏了--- 之 virtual 析构
- 内存泄漏分析 mat 使用 activity泄漏
- 实战Java内存泄漏问题分析 -- hazelcast2.0.3使用时内存泄漏 -- 1
- java-算法的基本定义
- windows下文档格式转换 为unix文档格式注意事项
- #761 – FlowDirection如何影响HorizontalContentAlignment(How FlowDirection Affects HorizontalContentAlign)
- jsp+javaBean+servlet+mysql完整的实现一个登录功能
- 计蒜课 跳跃游戏
- 你使用Rxjava时,内存泄漏了吗?
- 使用ADB遇到的问题
- android databinding不出现具体错误':app:compileDebugJavaWithJavac'. > Compilation failed;
- 申请深圳户口真他妈麻烦
- lintcode Permutation Index
- 【ArcGIS】利用ArcGIS 的Java API实现SDE数据库的直连和服务连接
- Go的组合继承
- 我使用过的框架的基本涵盖所有版本CDN网址
- 最小生成树 堆优化的Prim算法