使用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