使用CompletableFuture线程池处理数据出现内存报错【java.lang.OutOfMemoryError】?

代码很简单就,为了提升效率,使用线程并行处理大块的数据 ,项目运行后,一开始不会报错(futures.add(CompletableFuture.supplyAsync(()这行报错),运行了一段时间后就会出现图中报错,找不到什么原因,很少用CompletableFuture,之前都没发生这样的问题,从现象来看像是累计内存的占用未释放的感觉,一开始我是数量20, 线程数20, 后来改成,100和线程数10,依然在运行了一段时间后报错。。。有没有懂得大神帮帮忙看一下

List<HOProdCenterInfoVOData> getProductsInfo = new ArrayList<>();

List<List<String>> itemNoListList = new ArrayList<>();

if(itemNoList != null) {

itemNoListList = Lists.partition(itemNoList, 20);//每次数量

List<CompletableFuture<List<HOProdCenterInfoVOData>>> futures = new ArrayList<>();

ExecutorService threadPool = Executors.newScheduledThreadPool (20);//线程数

itemNoListList.stream().forEach(oneTimeItemNoList -> {

futures.add(CompletableFuture.supplyAsync(() -> {

if(oneTimeItemNoList.size() > 0) {

return HOProdCenterClient.getProductsInfo(storeNo, oneTimeItemNoList, 0, 1000);

}

return null;

},threadPool).handle((res, e) -> {

if(e != null || res == null) {

return new ArrayList<HOProdCenterInfoVOData>();

}

return res;

}));

});

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

for (CompletableFuture<List<HOProdCenterInfoVOData>> future : futures) {

try {

List<HOProdCenterInfoVOData> oneTimeProductsInfo= future.get();

getProductsInfo.addAll(oneTimeProductsInfo);

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

continue;

}

}

}

P.S.2022.12.05补充:
不好意思漏说一些基本信息,刚开始服务器是8核8G的jvm内存4G,后来因为这个原因还升级内存到16G,jvm内存8G,依然还是报这个错。。后来从查了一下,阿里是禁止使用Executors来创建线程池的,使用Executors创建就会又OOM的风险,需要使用ThreadPoolExecutor来自定义创建线程池,所以线程池的创建我改成了下面这样,但是依然发生了同样的报错

ExecutorService pool = new ThreadPoolExecutor(17,80,1, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(80),

Executors.defaultThreadFactory(),

new ThreadPoolExecutor.DiscardOldestPolicy());

还有一个重要的点, 可能是这个引起,这个段代码并不是JOB中的,而是页面显示的代码,因为一个页面某些需求的缘故是不分页的,虽然数量最大页也就几千条,但中间需要调用很多接口就造成了显示缓慢,所以我才想到加入线程处理。


回答:


看报错应该是无法创建新的本地线程了,addWorker()时报错了。

是什么导致无法创建线程?
我认为是cpu没有资源了,根本原因如下:

 ExecutorService threadPool = Executors.newScheduledThreadPool (20);

线程池居然声明在方法体内

可以看到他的核心线程数被设置了。
一般情况下,不会回收核心线程,因为线程池本身就是实现线程的复用

建议:
ExecutorService threadPool = Executors.newScheduledThreadPool (20);
这个不要放在方法体内,而是声明为类的属性或者单独维护


回答:

CompletableFuture的默认线程池,只有在双核以上的机器内才会使用。在双核及以下的机器中,会为每个任务创建一个新线程,等于没有使用线程池,且有资源耗尽的风险

private static final Executor asyncPool = useCommonPool ?

ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

使用CompletableFuture时推荐使用自定义线程池


回答:

1、在方法内创建的线程池,不会自动关闭,每次运行方法,都会创建一个新的线程池且不会自动关闭(可以在main方法中新建一个线程池测试,线程池任务运行完后,main方法会一直运行,不会自动关闭),可以监控一下线程池的数量,看是否是每次运行线程只增不减,如果要在方法内创建线程池,可以在finally中手动关闭一下线程池threadPool.shutdown();2、这个周期性线程池ScheduledThreadPool感觉不适用与这个场景;

参考:方法中创建线程池,方法结束后线程池会被垃圾回收吗?


回答:

    ExecutorService threadPool = Executors.newScheduledThreadPool (20);//线程数

这行代码 移动到方法外。就完事了

ExecutorService pool = new ThreadPoolExecutor(17,80,1, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(80),

Executors.defaultThreadFactory(),

new ThreadPoolExecutor.DiscardOldestPolicy());

或者是你修改后的这行。要放在全局变量中。不应该在每个方法中创建线程池。

以上是 使用CompletableFuture线程池处理数据出现内存报错【java.lang.OutOfMemoryError】? 的全部内容, 来源链接: utcz.com/p/944884.html

回到顶部