解决retrofit OKhttp创建大量对外连接时内存溢出

2024-04-06 12:48

本文主要是介绍解决retrofit OKhttp创建大量对外连接时内存溢出,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这个问题是这样发生的,我的表中有一批数据,量级较大,数百万个,它们有个地址Address字段,标明了地理位置。我需要对这一批数据根据地址去百度或者高德地图去查询经纬度,并且保存下来。
原本是直接分页读取该表,每次读取几百条,然后一条一条去获取经纬度并且保存。后来发现实在太慢,一秒也就能处理个三五条。所以开启了多线程,大约30个线程,每个线程处理不同id范围的数据。
此时问题出现了,每个线程中都有for循环来分页读取DB中的地址数据,然后每条数据都要去百度地图请求一次,网络请求用的是retrofit,retrofit是包装的OKHttp。
这里写图片描述
这里就是构建retrofit的地方,都是一些普通的配置。
然后运行程序后,发现线程数急剧上升,没几秒就跑到了2000多个线程,然后发生内存溢出,程序就挂掉了。
这里写图片描述
这是刚启动项目时,线程数只有50多个。当开启多线程任务后
这里写图片描述
可以看到线程数干到2000多时程序崩了

java.lang.OutOfMemoryError: unable to create new native threadat java.lang.Thread.start0(Native Method)at java.lang.Thread.start(Thread.java:714)at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1368)at okhttp3.ConnectionPool.put(ConnectionPool.java:153)at okhttp3.OkHttpClient$1.put(OkHttpClient.java:163)at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:201)at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:121)at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:100)at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)at com.mindata.ecserver.global.http.RetrofitServiceBuilder.lambda$generateClient$0(RetrofitServiceBuilder.java:72)at com.mindata.ecserver.global.http.RetrofitServiceBuilder$$Lambda$11/620324267.intercept(Unknown Source)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)at okhttp3.RealCall.execute(RealCall.java:69)at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)at com.mindata.ecserver.global.http.CallManager.execute(CallManager.java:25)

通过监控界面可以看到,大量的线程OKHttp ConnectionPool,也是导致内存溢出的主要原因。
这里写图片描述
为什么会有这么多的OkHttp ConnectionPool呢。通过分析代码发现,我有30个线程,每个线程里会分配数万的数据来进行百度地图的请求,每次循环,都会发起一个retrofit网络请求去访问百度,这样很快就有数千个http请求出去了。
伪代码大概是这样的

public BaiduCoordinateService getBaiduCoordinateService(RequestProperty requestProperty) {return generateRetrofit(requestProperty).create(BaiduCoordinateService.class);}这是构建百度地图请求的,我的用法是for(int i = 0; i < 5000; i++) {getBaiduCoordinateService(xxx).fetchBaiduCoordinate();}

网络请求多没什么,关键是我的每个请求得到结果后就不用了,但是系统依旧保持了这个请求的线程没有释放,直接导致线程池越来越大,很快超过最大限制就崩溃了。
通过各方查证,有的说,请求header里的Connection,写的是keep alive,导致了长连接,所以我把构建retrofit的header的地方改成了Connection为close,然而没什么卵用。依旧是上面的问题,很快线程数超过就崩溃了。
后来开始调查OkHttpClient的ConnectionPool,这个就是OkHttp网络请求的线程池,在OkHttpClient源码中可以看到

public OkHttpClient.Builder connectionPool(ConnectionPool connectionPool) {if (connectionPool == null) {throw new NullPointerException("connectionPool == null");} else {this.connectionPool = connectionPool;return this;}}

在OkHttpClient的源码中,默认的构造方法里可以看到默认最大线程空闲数是5,keepAlive时间为5分钟。也就是发起一次网络连接后,5分钟内不会断开连接。
这里写图片描述
那么问题就出在这里了,我在短时间内发起了大量网络连接,每个是一个线程,而且每个都默认保存5分钟,很快线程数就超标了。
考虑到我的每次请求都是一次性的,所以我修改了ConnectionPool的keepAliveDuration时间,让每次连接1秒后就关闭。
这里写图片描述
之后再次运行程序,发现OK了,线程数最大也没超过200,程序也没再抛出过outofmemery异常。
后来又仔细回想了一下,发现哪里怪怪的,为毛我会有这么多的ConnectionPool连接呢?
又回头看了一下创建retrofit的请求service的地方,发现了最大的问题所在,我在对数据库循环过程中,每条数据都创建了一个service,如上面的伪代码那里所写,每一条数据都走了一遍generateClient。这才是罪魁祸首!因为我的项目中,多个地方会使用retrofit,会创建baseUri不同的请求,所以我想成了每次请求都创建一个客户端的方式。没想到这样会创建一个额外的线程。
最终解决方式是,我又把设置OkHttp5分钟那里给恢复默认了,然后对于baidu的请求,只创建一个service,而不是在循环里去创建多个retrofit客户端。
最后再次运行,这下线程更少了,只剩90个了。

这篇关于解决retrofit OKhttp创建大量对外连接时内存溢出的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/879922

相关文章

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM

python如何创建等差数列

《python如何创建等差数列》:本文主要介绍python如何创建等差数列的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python创建等差数列例题运行代码回车输出结果总结python创建等差数列import numpy as np x=int(in

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

IDEA Maven提示:未解析的依赖项的问题及解决

《IDEAMaven提示:未解析的依赖项的问题及解决》:本文主要介绍IDEAMaven提示:未解析的依赖项的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录IDEA Maven提示:未解析的依编程赖项例如总结IDEA Maven提示:未解析的依赖项例如

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

如何使用Maven创建web目录结构

《如何使用Maven创建web目录结构》:本文主要介绍如何使用Maven创建web目录结构的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录创建web工程第一步第二步第三步第四步第五步第六步第七步总结创建web工程第一步js通过Maven骨架创pytho