Rxjava和Retrofit结合使用大量请求时候出现OOM的问题

2023-05-16

在使用RxJava+Retrofit的过程中,出现了OOM的问题,报错日志如下:

java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try
again
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:733)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:970)
at java.util.concurrent.ThreadPoolExecutor.ensurePrestart(ThreadPoolExecutor.java:1611)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:342)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:579)
at java.util.concurrent.ScheduledThreadPoolExecutor.submit(ScheduledThreadPoolExecutor.java:680)
at io.reactivex.internal.schedulers.NewThreadWorker.scheduleActual(NewThreadWorker.java:145)
at io.reactivex.internal.schedulers.IoScheduler$EventLoopWorker.schedule(IoScheduler.java:239)
at io.reactivex.Scheduler.scheduleDirect(Scheduler.java:203)
at io.reactivex.Scheduler.scheduleDirect(Scheduler.java:179)
at io.reactivex.internal.operators.observable.ObservableSubscribeOn.subscribeActual(ObservableSubscribeOn.java:36)
at io.reactivex.Observable.subscribe(Observable.java:12267)
at io.reactivex.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:32)
at io.reactivex.Observable.subscribe(Observable.java:12267)
at io.reactivex.internal.operators.observable.ObservableObserveOn.subscribeActual(ObservableObserveOn.java:45)
at io.reactivex.Observable.subscribe(Observable.java:12267)

原因是在创建大量请求时候会创建大量的线程, 在手机上由于手机对进程有线程数量的限制导致了闪退.

第一种解决方案

可以查看:
https://github.com/ReactiveX/RxAndroid/issues/387

最佳答案是:

JakeWharton commented on 9 Aug 2017

The problem is that Schedulers.io() uses a cached thread pool without
a limit and thus is trying to create 1500 threads. You should consider
using a Scheduler that has a fixed limit of threads, or using RxJava
2.x’s parallel() operator to parallelize the operation to a fixed number of workers.

If you’re using raw Retrofit by default it uses OkHttp’s dispatcher
which limits the threads to something like 64 (with a max of 5 per
host). That’s why you aren’t seeing it fail.

If you use createAsync() when creating the RxJava2CallAdapterFactory
it will create fully-async Observable instances that don’t require a
subscribeOn and which use OkHttp’s Dispatcher just like Retrofit would
otherwise. Then you only need observeOn to move back to the main
thread, and you avoid all additional thread creation.

主要意思就是: 如果只使用retrofit是不会出现这个问题的, 由于rxjava自身没有对线程数量做限制所以可能会出现这个问题.

这个解决方法主要有两点:
首先是在创建retrofit的时候使用 createAsync()

if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(baseurl)       //设置远程地址
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
                    .client(OkHttpHelper.getInstance().getOkHttpClient())
                    .build();
}

此时将创建完全异步的可见实例,这些实例不需要订阅,并且使用OkHttp的Dispatcher,就像Retrofit一样。
不需要订阅就表示不需要调用 .subscribeOn(getScheduler())

Observable<T> result = responseBody
//      .subscribeOn(Schedulers.io())    //如果使用RxJava2CallAdapterFactory.createAsync(),则不需要这一句
        .map(new Function<ResponseBody, T>() {
            @Override
            public T apply(ResponseBody responseBody) throws Exception {
                String string = responseBody.string();
                return new Gson().fromJson(string, clazz);
            }
        })
        .observeOn(AndroidSchedulers.mainThread());

最后的结果:

anilmaddala commented on 9 Aug 2017

Thanks @JakeWharton tried the createAsync() and removed subscribeOn to
Scheduler.io and I am not seeing the crash. Thanks.

I also posted the question in StackOverflow. If you can, please post
your answer there and I will mark it as a chosen answer.

这种解决方法有两点, 首先使用 RxJava2CallAdapterFactory.createAsync() 来自动创建异步, 然后不使用 .subscribeOn(Schedulers.io()) ,这样rxjava的线程数量就会做出和retrofit一样的限制, 因为retrofit不出问题, 则rxjava不出问题.

一个重要的后续

by 2020.8
最近,在升级项目架构的时候,也升级了rxjava3, 于是将retrofit的adapter也进行了升级,进行了如下引用

api "com.squareup.retrofit2:adapter-rxjava3:2.9.0"

同时,在创建retrofit的时候

//使用RxJava2版本
if (retrofit == null) {
    retrofit = new Retrofit.Builder()
                    .baseUrl(baseurl)      
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.createAsync())
                    .client(OkHttpHelper.getInstance().getOkHttpClient())
                    .build();
}

升级到

//使用RxJava3版本
if (retrofit == null) {
    retrofit = new Retrofit.Builder()
                    .baseUrl(baseurl)      
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
                    .client(OkHttpHelper.getInstance().getOkHttpClient())
                    .build();
}

发现网络请求不到数据了, 查看日志发现原因是: 在主线程请求了网络数据

比较蒙,经过排查, 原因是 RxJava3CallAdapterFactory 和 RxJava2CallAdapterFactory的调用方法是相反的,通过查看源码可以知道:

//RxJava2CallAdapterFactory
/**
 * 返回一个创建不操作任何调度器的同步可观察对象的实例
 * Returns an instance which creates synchronous observables that do not operate on any scheduler
 * by default.
 */
public static RxJava2CallAdapterFactory create() {
  return new RxJava2CallAdapterFactory(null, false);
}


//返回一个创建异步可观察对象的实例
/** Returns an instance which creates asynchronous observables. */
public static RxJava2CallAdapterFactory createAsync() {
  return new RxJava2CallAdapterFactory(null, true);
}

而在RxJava3CallAdapterFactory 中

//RxJava3CallAdapterFactory 
/**
 * 返回一个运行在后台线程上的异步可观察对象的实例
 * Returns an instance which creates asynchronous observables that run on a background thread by
 * default. Applying {@code subscribeOn(..)} has no effect on instances created by the returned
 * factory.
 */
public static RxJava3CallAdapterFactory create() {
  return new RxJava3CallAdapterFactory(null, true);
}

/**
 * 返回一个创建不操作任何调度器的同步可观察对象的实例
 * Returns an instance which creates synchronous observables that do not operate on any scheduler
 * by default. Applying {@code subscribeOn(..)} will change the scheduler on which the HTTP calls
 * are made.
 */
public static RxJava3CallAdapterFactory createSynchronous() {
  return new RxJava3CallAdapterFactory(null, false);
}

也就是说, 可能是因为RxJava2CallAdapterFactory 中默认没有对rxjava在使用

.subscribeOn(Schedulers.io())

方法的时候做出限制,导致很多人提交了 issues, 所以在RxJava3CallAdapterFactory 中默认对此作了限制,当然你自己也可以扩展,使用

.addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())

来自定义, 就像最初使用RxJava2 一样.
总之,RxJava3CallAdapterFactory对此默认作了限制,现在,可以放心的使用 create() 方法即可, 同时在RxJava中无需再使用

.subscribeOn(Schedulers.io())

end.

第二种解决方案

对rxjava的 Scheduler 进行自定义限制线程数量

Observable<T> result = responseBody
        .subscribeOn(getScheduler())
        .map(new Function<ResponseBody, T>() {
            @Override
            public T apply(ResponseBody responseBody) throws Exception {
                String string = responseBody.string();
                return new Gson().fromJson(string, clazz);
            }
        })
        .observeOn(AndroidSchedulers.mainThread());
private static Scheduler mScheduler;

private static Scheduler getScheduler() {
    if (mScheduler == null) {
        synchronized (HttpLoaderUtils.class) {
            if (mScheduler == null) {
                mScheduler = Schedulers.from(Executors.newFixedThreadPool(15));
            }
        }
    }
    return mScheduler;
}

自行对Scheduler的数量进行限制.
可以查看:
Android 栈内存溢出bug

总结

两种方法的解决核心都是对rxjava自身的线程数量的限制.

第一种方案通过Retrofit配置adapter对Rxjava产生影响,使其线程的调度用Retrofit本身提供的线程,其中有一句:

/**
 * 返回一个运行在后台线程上的异步可观察对象的实例
 * Returns an instance which creates asynchronous observables that run on a background thread by
 * default. Applying {@code subscribeOn(..)} has no effect on instances created by the returned
 * factory.
 */
public static RxJava3CallAdapterFactory create() {
  return new RxJava3CallAdapterFactory(null, true);
}

也就是说用了这个以后即使在RxJava中调用 subscribeOn(…) 方法也不会影响返回的实例.

第二种方案是通过RxJava自身调度来限制其所使用的线程数量,因为 .subscribeOn(Schedulers.io())方法不对线程数量做任何限制, 所以当并发请求的接口数量过多就可能会出现OOM的情况,通过线程池来限制也是一种解决方案.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rxjava和Retrofit结合使用大量请求时候出现OOM的问题 的相关文章

  • webRTC中的coturn服务安装

    目录 1 先准备一台云主机 2 安装coturn的依赖 2 1 依赖软件准备 2 1 安装依赖组件 2 2 安装coturn的持久化保存用户信息库 3 安装coturn 4 coturn配置 4 1 创建用户 4 2 配置说明 4 3 收集
  • Git配置.gitignore忽略文件

    git设置忽略文件和目录有两种方式 xff0c 一种是项目所有人员共用的的 xff0c 一种是开发自己使用的 第一种 xff0c 所有开发者共用的需要把设置设定在 gitignore该文件中 第二种 xff0c 开发者个人使用的忽略配置 x
  • 线程池实现原理

    创建线程有哪几种方式 一 继承Thread类创建线程类 xff08 1 xff09 定义Thread类的子类 xff0c 并重写该类的run方法 xff0c 该run方法的方法体就代表了线程要完成的任务 因此把run 方法称为执行体 xff
  • linux查看cpu与内存

    查看cpu 第一种方法 xff1a top命令法 1 首先执行top命令 xff1b 2 在top命令的显示界面 xff0c 按数据键1 xff0c 即可查看到当前系统中的总cpu数 xff1b 第二种方法 xff1a 通过proc文件系统
  • Element 布局组件el-row和el-col 详解

    1 背景 element的布局方式与bootstrap原理是一样的 xff0c 将网页划分成若干行 xff0c 然后每行等分为若干列 xff0c 基于这样的方式进行布局 xff0c 形象的成为栅栏布局 区别是element可将每行划分为24
  • netbeans中配置maven

    deploy 发布到远程maven库 本节默认maven库为nexus netbeans中按ctrl 43 1 xff0c 打开Project窗口 xff1b 在Project窗口中找到相关的project或module 在项目名上点击鼠标
  • 订阅关系一致

    订阅关系一致指的是同一个消费者Group ID下所有Consumer实例所订阅的Topic Tag必须完全一致 如果订阅关系不一致 消息消费的逻辑就会混乱 甚至导致消息丢失 本文提供订阅关系一致的正确示例代码以及订阅关系不一致的可能原因 帮
  • java之PO,VO,TO,QO,BO等

    PO persistant object 持久对象 在 o r 映射的时候出现的概念 xff0c 如果没有 o r 映射 xff0c 没有这个概念存在了 通常对应数据模型 数据库 xff0c 本身还有部分业务逻辑的处理 可以看成是与数据库中
  • 多生产者多消费者问题的无锁队列实现

    背景 代码根据论文 Implementing Lock Free Queues 复现 背景知识博客 xff1a 左耳朵耗子博客 https coolshell cn articles 8239 html 代码地址 xff1a https g
  • Ubuntu18下Github+Hexo搭建博客教程

    我的博客 xff0c 欢迎来访 xff1a www zxwsbg cn 搭建 安装git nodejs sudo apt get install git sudo apt get install nodejs sudo apt get in
  • linux中提供了PF_PACKET接口可以操作链路层的数据

    http blog sina com cn s blog 82f2fc28010132og html sock raw xff08 注意一定要在root下使用 xff09 原始套接字编程可以接收到本机网卡上的数据帧或者数据包 对于监听网络的
  • 分享52个Java源码,总有一款适合您

    Java源码 分享52个Java源码 xff0c 总有一款适合您 下面是文件的名字 xff0c 我放了一些图片 xff0c 文章里不是所有的图主要是放不下 xff0c 大家下载后可以看到 源码下载链接 xff1a https pan bai
  • 抽象类中的方法该如何实现呢?

    本节通过一个案例来学习如何实现抽象类中的方法 xff0c 具体步骤如下 xff1a 1 创建Animal类 创建一个Animal抽象类 xff0c 并在类中定义一个抽象call 方法 xff0c 如文件3 25所示 文件3 25Animal
  • zset类型的底层数据结构的实现

    参考资料 xff1a redis中zset底层实现原理 渣渣 CSDN博客 zset底层数据结构 redis的zset数据结构 xff1a 跳表 知乎 zset类型的底层数据结构的实现 xff1f zset是Redis提供的一个非常特别的数
  • XD软件都有哪些基础操作?

    下面我们来学习一下XD软件的基础操作 xff0c 包括资产面板的功能 交互动作 一键切图等等 1 重复网格 xff08 1 xff09 重复网格可智能复制其选择对象 xff0c 并批量更换图片 修改文字 之间距离等 xff08 2 xff0
  • 3分钟掌握7个XD基础操作

    下面我们来学习一下XD软件的基础操作 xff0c 包括资产面板的功能 交互动作 一键切图等等 1 重复网格 xff08 1 xff09 重复网格可智能复制其选择对象 xff0c 并批量更换图片 修改文字 之间距离等 xff08 2 xff0
  • 目标跟踪常用算法——EKF篇

    目录 1 扩展卡尔曼滤波算法 1 1 扩展卡尔曼滤波算法简单介绍 1 2 扩展卡尔曼滤波算法流程 1 3 扩展卡尔曼滤波算法仿真分析 2 参考文献 1 扩展卡尔曼滤波算法 1 1 扩展卡尔曼滤波算法简单介绍 对于非线性滤波问题 xff0c
  • 人工智能概述

    目录 什么是人工智能实现人工智能的方法逻辑编程机器学习深度学习机器学习和深度学习的区别 人工智能的分类如何实现人工智能 什么是人工智能 人工智能 又被称为机器智能 xff0c 是一种综合计算机科学 统计学 语言学等多种学科 xff0c 使机
  • java注解(annotation)的执行顺序

    可以在切面上使用 64 Order注解 如 64 Component 64 Aspect 64 Order 1 public class Aspect1 64 Component 64 Aspect 64 Order 2 public cl
  • Eclipse 常用快捷键 (动画讲解)

    Eclipse 常用快捷键 动画讲解 Eclipse有强大的编辑功能 xff0c 工欲善其事 xff0c 必先利其器 xff0c 掌握Eclipse快捷键 xff0c 可以大大提高工作效率 小坦克我花了一整天时间 xff0c 精选了一些常用

随机推荐

  • javaweb三大框架SSH

    1 MVC三层架构 xff1a 模型层 xff0c 控制层和视图层 模型层 xff0c 用Hibernate框架让来JavaBean在数据库生成表及关联 xff0c 通过对JavaBean的操作来 对数据库进行操作 xff1b 控制层 xf
  • HTTP请求方式及区别

    GET 向特定的路径资源发出请求 xff0c 数据暴露在url中 POST 向指定路径资源提交数据进行处理请求 xff08 一般用于上传表单或者文件 xff09 xff0c 数据包含在请求体中 OPTIONS 返回服务器针对特定资源所支持的
  • C++实现邮件群发的方法

    这篇文章主要介绍了C 43 43 实现邮件群发的方法 较为详细的分析了邮件发送的原理与C 43 43 相关实现技巧 非常具有实用价值 需要的朋友可以参考下 本文实例讲述了C 43 43 实现邮件群发的方法 分享给大家供大家参考 具体如下 x
  • Asp.Net Core IIS发布后PUT、DELETE请求错误405.0 - Method Not Allowed 因为使用了无效方法(HTTP 谓词)

    Asp Net Core IIS发布后PUT DELETE请求错误405 0 Method Not Allowed 因为使用了无效方法 HTTP 谓词 一 在使用Asp net WebAPI 或Asp Net Core WebAPI 时 x
  • Java lambda表达式使用笔记

    package com allsaints music admin import com allsaints music admin service entrymgr bak Student import lombok Data impor
  • .NET Framework 与 .NET Core 的区别与联系

    当今 net 生态系统如下 xff1a 从上面图中我们可以看到 net 主要分为三个部分 net FrameWork net Core Xamarin XAMARIN 主要用来构建APP的 xff08 包括IOS xff0c Android
  • .net 代码命名规范

    CAST 源代码命名规范手册 v1 1 Pascal 命名 xff1a 每一个单词首字母必须大写 Camel 命名 xff1a 第一个单词首字母小写 xff0c 其余单词首字母必须大写 任何命名必须优先使用英文单词表达意思 xff0c 若不
  • SpringBoot项目配置文件编写方式参考

    背景 为防止出现各环境配置文件不同步的情况 xff0c 现根据实际开发情况 xff0c 制定该配置文件编写参考 介绍 SpringBoot使用一个全局的配置文件 xff0c 配置文件名是固定的 xff1b application prope
  • xshell 连接 Linux kvm图形界面报错问题

    如下的报错 zyq 64 zyq sudo virt manager zyq 64 zyq virt manager 18561 Gtk WARNING cannot open display localhost 10 0 以上前提是需先安
  • 一文让你看懂Golang如何打造实时聊天系统

    项目截图 简介 在本次课程中 xff0c 我们来学习使用WebSocket来打造一个实时聊天系统 我们会从一下几个方面来进行学习 xff1a 什么是websocket xff1b Websocket与传统的HTTP协议有什么区别 xff1b
  • 【IBM MQ】使用IBM MQ远程连接时报错AMQ 4043解决思路

    我使用IBM MQ客户端远程连接队列管理器时 xff0c 报错 AMQ 4043 xff0c 百度基本找不到解决办法 xff0c 唯一一个解决方法是这个 xff08 https stackoom com question 1sroR xff
  • Arch Linux安装桌面及常用软件

    Arch Linux安装桌面及常用软件 安装桌面环境 显卡驱动 span class token comment 查看显卡情况 span lspci k span class token operator span span class t
  • Python 报错处理 paramiko.ssh_exception.SSHException: Error reading SSH protocol banner

    使用多进程启动多个ssh报错 xff1a Exception client Error reading SSH protocol banner Errno 104 Connection reset by peer During handli
  • 信息加密(简单的字母转换)

    题目 在传递信息的过程中 xff0c 为了加密 xff0c 有时需要按一定规则将文本转换成密文发送出去 有一种加密规则是这样的 xff1a 1 对于字母字符 xff0c 将其转换成其后的第3个字母 例如 xff1a A D xff0c a
  • js按钮绑定点击事件

    1 第一种 34 btn 34 click function 操作 2 第二种 document getElementById 39 foo 39 addEventListener 39 click 39 function 3 第三种 xf
  • 微信小程序与微信公众号同一用户登录问题

    微信小程序与微信公众号同一用户登录问题 最近在做微信小程序与微信公众号登录合并的接口 整理相关资料以及个人认识的心得写了这篇文章与大家一起分享 首先 xff0c 简单说下我遇到的问题是我们的程序调用微信小程序得到openid 然后通过ope
  • 快速编译system.img和boot.img的方法【转】

    本文转载自 xff1a http www cnblogs com wanqieddy archive 2012 10 22 2734024 html 快速编译system img xff0c 可以使用这个命令 xff1a make syst
  • SSM----SpringMVC

    SpringMVC 1 什么是SpringMVC Spring MVC属于SpringFrameWork的后续产品 xff0c 已经融合在Spring Web Flow里面 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模
  • mvp契约类

    public class MainActivity extends AppCompatActivity implements IContract IView private IContract IPresenter presenter 64
  • Rxjava和Retrofit结合使用大量请求时候出现OOM的问题

    在使用RxJava 43 Retrofit的过程中 出现了OOM的问题 报错日志如下 java lang OutOfMemoryError pthread create 1040KB stack failed Try again at ja