• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

记一次线程过多引起的OOM问题的处理

武飞扬头像
晃荡半瓶醋
帮助1

最近遇到了一次由于线程过多导致的OOM问题,在此进行记录和分享,避免下次出现相同的问题。

起因

最近测试人员在对APP进行弱网环境下的测试,结果在测试的过程中多次出现了crash,crash的堆栈信息如下图。

学新通

从图中的信息可以看到,是由于OOM导致的crash,且OOM的原因是线程过多。

第一次问题定位

考虑到这个问题的出现是在测试进行弱网环境测试的过程中出现的,脑海中出现的第一反应就是,这些线程大概率是网络请求的线程,因为弱网的原因,网络请求速度慢,线程被占用堆积,越积越多,最后导致了OOM的出现。

基于这样的想法,我就从网络请求部分的代码查起,我们APP使用的网络框架是retrofit rxjava,其中,rxjava的版本是rxjava2。基本上项目内所有的网络请求代码,切换到子线程进行网络请求的方式都是使用subscribeOn(Schedulers.io())来实现,乍一看没有什么问题,Schedulers.io()是为IO操作设计的线程池,而网络请求也确实是IO操作为主,但是当我们仔细看Schedulers.io()的定义,我们可以发现这是由无边界线程池作为支撑的一个Scheduler,它适用于非CPU密集的I/O工作,比如访问文件系统、执行网络调用、访问数据库等等。这个Scheduler是没有限制的,它的线程池可以按需一直增长。这里划重点,Schedulers.io()有点类似于java线程池中的newCachedThreadPool,如果没有可以复用的线程,系统会不停地创建新的线程,最后导致线程过多产生OOM问题。

解决方案

既然问题出现的原因是网络请求在Schedulers.io()上执行,创建的线程数没有限制,导致线程过多,那么我就换一个线程池来实现网络请求的线程管理就可以解决问题了。这里,我们注意到rxjava有一个方法是Schedulers.from(Executor executor),使得任务执行在自定义的线程池中,所以我们在网络请求管理类的单例中,使用newFixedThreadPool创建一个固定大小的线程池,所有的网络请求都在这个线程池中执行。

后来,我在网络上搜索相关信息的过程中,也看到了别人遇到相同的问题以及在github上向RxAndroid提的issue,JakeWharton对此做出了回复,原因和之前我们分析的一样,同时,他提出了另一种解决方案,使用RxJava2CallAdapterFactory的createAsync方法来构造Retrofit实例,这样就会创建一个完全异步的Observeable,不需要再把它单独丢到自己指定的线程调度器当中去,只需要observeOn主线程即可。但是这样的改法,subscribeOn方法对createAsync最终构造出来的Observeable是没有影响的,也就是说,只要你用了createAsync,即便后续再调用subscribeOn也不会对网络请求的线程造成影响,一直到你调用observeOn之前(包括map等操作),线程都不会切换。

第二次问题定位

在做了方案一的修复以后,OOM的问题得到了改善,但是并没有完全解决OOM问题,因此,开始了第二次问题的定位。在第二次定位时,使用android studio自带的profiler工具分析了cpu运行情况,查看有哪些线程在侵占资源,结果发现在线程数不断上涨时,线程列表里出现了大量的OkHttp ConnectionPool的线程,正常来说,OkHttpBuilder在创建OkHttp Client的时候,应该只会创建一个线程数为5,存活时长为5分钟的线程池,现在出现了大量的OkHttp ConnectionPool的线程肯定是OkHttp Client创建的时候发生了问题。

解决方案

最后发现,项目中由于有一系列的请求因为baseUrl是平台接口获取到的,所以对于这一系列动态baseUrl的接口,开发人员每次都会创建一个新的OkHttp Client,最后导致了线程越积越多,最后OOM。为了减少代码的改动,解决方案最后使用了对这些OkHttp Client创建时设置一个公用的线程池来作为connectionPool,不再重复创建。

闲聊几句

现在网上学习的资料很多,互相之间也都是抄来抄去。看现在网上基本上所有关于retrofit rxjava的框架,入门介绍等文章,全部都是教开发者网络请求使用Schedulers.io()来切换到子线程执行,把所有人带入了坑里,其实仔细看rxjava对他几种Scheduler的描述,就可以避免这样的问题,所以对网上千篇一律的入门文章,阅读时还是要保持有慎重的态度。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgccebk
系列文章
更多 icon
同类精品
更多 icon
继续加载