自动挡线程池OOM最佳实践

Executors 类创建线程池的弊端

Executors创建线程池示例

Executors工具类始于JDK 1.5,是Java在java.util.concurrent包中提供的线程池类,Executors可用于快速创建线程池,既然它这么方便的让开发者使用线程池,但是为什么《阿里巴巴 Java 开发手册》中提到,禁止使用这些方法来创建线程池呢?

java 复制代码
    @GetMapping("oom1")
    public void oom1() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        for (int i = 0; i < 100000000; i++) {
            executorService.execute(() -> {
                String payload = IntStream.rangeClosed(1, 1000000).mapToObj(__ -> "a").collect(Collectors.joining("")) + UUID.randomUUID().toString();
                try {
                    TimeUnit.HOURS.sleep(1);
                } catch (InterruptedException e) {
                }
                log.info(payload);
            });
        }

        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.HOURS);
    }

代码中在for循环中使用Executors工具类来初始化一个单线程的 FixedThreadPool,循环 1 亿次向线程池提交任务,每个任务都会创建一个比较大的字符串然后休眠一小时,调用该接口一段时间后发生了OOM,下文我们通过dump日志来分析导致OOM的原因。

使用IDEA分析OOM原因

我们可以在IDEA中通过调整Spring Boot程序的jvm参数以快速复现OOM的原因。

ruby 复制代码
-Xms256m
-Xmx512m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/liutaixiang/Documents/dev/logs

参数解读:

  • -Xms256m 设置JVM初始堆内存大小为256MB
  • -Xmx512m 设置JVM最大堆内存为512MB,如果应用内存需求超过512MB,会触发OutOfMemoryError
  • -XX:+HeapDumpOnOutOfMemoryError 在发生OutOfMemoryError时自动生成堆转储文件(Heap Dump),堆转储文件记录了JVM堆内存的完整快照,便于后续分析内存泄漏或对象占用过大的问题
  • -XX:HeapDumpPath=/Users/liutaixiang/Documents/dev/logs 指定堆转储文件的保存路径,该路径需要根据自身实际情况配置

配置完成后可启动SpringBoot项目调用接口等待OOM,待项目发出OOM错误后,可以去配置的路径下查找dump文件,一般以hprof为后缀,打开IDEA中的Profiler,将文件拖入Profiler即可,需要注意的是不同版本IDEA操作可能略有不同,可自行百度操作方式也可参考IDEA官网给出的操作教程。

IDEA官网教程

GC Roots 选项卡显示类列表及其对应的垃圾收集器根对象。此信息是在拍摄快照时无法进行垃圾收集的所有对象的概述。例如,查看哪个类加载器占用应用程序服务器中的大部分内存消耗可能很有用。

打开dump文件后可以看到程序在出现OOM错误时的资源占用情况,我们分配的最大堆内存是512MB,分析dump文件可以发现java.util.concurrent.LinkedBlockingQueue占用了516MB的内存,并且频繁触发GC Root,我们查看Executors类内部的源码实现可以知晓Executors正是使用了LinkedBlockingQueue作为队列,并且LinkedBlockingQueue的容量是一个Integer.MAX的无界队列,由此我们可以分析得到结论:Executors全自动挡的线程池使用不当会导致OOM。

相关推荐
爱勇宝9 分钟前
《道德经》第 13 章:别让评价系统控制你的心
前端·后端·产品
掘金者阿豪13 分钟前
集中式与分布式数据库怎么选?从架构原理到电科金仓的实践思考
前端·后端
.痞老板.36 分钟前
java ee--计算机是如何工作的
java·开发语言·java-ee
张彦峰ZYF1 小时前
升级了不等于变好了:AI回归测试的固定评测集、版本对比、质量阈值与CI门禁
人工智能·后端·模型或提示词升级·语言模型没有编译器与确定性输出·总体、切片与回退清单·红线、护栏与目标·ci门禁把判定写进合并路径
青梅味猪大肠1 小时前
【操作系统-60】临近适应算法
java·linux·开发语言·嵌入式硬件·算法·操作系统
CoderYanger1 小时前
Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)
java·数据库·spring·面试·职场和发展·java-ee·mybatis
程序员沉梦听雨1 小时前
【搜索引擎】Kibana使用指南
java·后端·学习笔记
Cosolar1 小时前
极简智库(MiniDocs):docsify、VitePress、Notion 等知识库搭建服务的完美替代
前端·后端·架构
Goboy2 小时前
夯爆了,我被一个一句话接入的免费可观测平台策反了,那一刻我决定放弃用了3年的Prometheus
后端·docker·kubernetes
奶油话梅糖2 小时前
打开项目 0.9 秒、内存只有 IDEA 的 1/12:OpenIDEA 用 Rust 把 IntelliJ IDEA 重造了一遍
java·rust·github·intellij-idea