使用RxJava+Retrofit实现HTTP轮询时出现java.lang.OutOfMemoryError问题
问题根因
- 核心原因是使用无并发限制的
flatMap操作符导致HTTP请求大量堆积:Observable.interval每隔500ms就会生成一个事件,flatMap会同时处理所有事件、并行发起网络请求,若网络环境差、单个请求响应时间超过500ms,请求就会持续累积。每个OkHttp请求都会占用线程、TCP连接等资源,累积到系统线程上限后就会抛出pthread_create相关的OOM异常。 - 额外可能的诱因:OkHttp未配置全局连接池复用、请求未设置超时,导致无效请求长期占用资源。
解决方案
1. 替换操作符避免并行请求
优先用switchMap替代flatMap:每当新的轮询事件触发时,自动取消上一个未完成的旧请求,保证同一时间最多只有一个活跃的轮询请求,完全避免资源堆积。
如果需要保证每个请求都得到响应后再发起下一个,可以用concatMap,强制请求串行执行。
2. 限制flatMap并发数(不替换操作符的兼容方案)
如果需要保留flatMap的使用,给flatMap增加第二个参数设置最大并发数为1,强制串行执行:
.flatMap(new Func1<Long, Observable<?>>() { @Override public Observable<?> call(Long aLong) { return CimaComm.getInstance().getService().getPaymentStatus(); } }, 1) // 限制最大并发为1
3. 补充基础配置优化
- 给OkHttp配置全局单例的连接池,复用TCP连接,减少线程创建开销
- 给网络请求增加合理的超时时间(建议10s以内),避免无响应的请求长期占用资源
- 补充线程调度指定:轮询属于IO操作,指定
subscribeOn(Schedulers.io()),如果是安卓端最后回调到主线程的话补充observeOn(AndroidSchedulers.mainThread())
修正后的示例代码
Observable.interval(500, TimeUnit.MILLISECONDS) // 用switchMap自动取消旧请求,同一时间仅保留一个活跃请求 .switchMap(new Func1<Long, Observable<?>>() { @Override public Observable<?> call(Long aLong) { return CimaComm.getInstance().getService().getPaymentStatus(); } }) .takeUntil(new Func1<Object, Boolean>() { @Override public Boolean call(Object o) { CimaPaymentStatusResponse responseState = (CimaPaymentStatusResponse) o; return responseState.status == CimaStatusEnum.completed || responseState.status == CimaStatusEnum.faulted; } }) // 指定IO线程执行请求,安卓端可加.observeOn(AndroidSchedulers.mainThread())切回主线程 .subscribeOn(Schedulers.io()) .subscribe(this::onPaymentPollingUpdate, this::handleCimaError);
内容的提问来源于stack exchange,提问作者giordy16
相关产品推荐
相关产品推荐

