You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch任务OOM问题排查:是否为内存泄漏所致?

Spring Batch批量任务OOM问题排查

问题背景

我使用Spring Batch实现了批量任务,但执行过程中遇到OOM问题,堆内存变化如下:
Heap memory graph

同时有内存快照:
Memory Snapshot

核心代码

@Tasklet
class SynchronizeStudentsTasklet(
    private val customStudentRepository: CustomStudentRepository,
    private val studentClient: StudentClient,
    private val lectureClient: LectureClient,
) : ItemTasklet<Mono<Student>> {
    private val logger = getLogger()
    private val years = (2018..Year.now().value).toList()
    private var number = 100000
    private var index = 0

    override fun read(): Mono<Student>? =
        years.getOrNull(index)
            ?.let { year ->
                if (number >= 102000) {
                    index += 1
                    number = 100000
                }

                "$year${number++}"
            }
            ?.let {
                studentClient.getStudentByNumber(it)
                    .flatMap { student ->
                            Mono.zip(
                                lectureClient.getAppliedLectureIdsByStudentNumber(it)
                                    .collectList(),
                                lectureClient.getPreAppliedLectureIdsByStudentNumber(it)
                                    .collectList()
                            ).map { (appliedLectureIds, preAppliedLectureIds) ->
                                student.copy(
                                    appliedLectureIds = appliedLectureIds.toHashSet(),
                                    preAppliedLectureIds = preAppliedLectureIds.toHashSet()
                                )
                            }
                    }
            }

    override fun write(chunk: Chunk<out Mono<Student>>) {
        Flux.concat(chunk.items)
            .flatMap { customStudentRepository.upsert(it) }
            .doOnNext { logger.info { it } }
            .collectList()
            .block()
    }
}

关键信息

  • 批量操作采用基于chunk的批处理方式
  • read()方法会发起6个HTTP请求(已通过WebClient限制并发)

疑问

基于以上信息,能否确定OOM的原因是内存泄漏?

补充排查细节

  1. 设置netty.leakDetection.level=advanced后,出现提示:LEAK: ByteBuf.release() was not called before garbage collection,但我们并未直接使用底层ByteBuf,仅通过WebClient发起请求。
  2. 确认OOM发生前执行了多次Full GC。

分析结论

目前的信息不足以直接确认OOM是内存泄漏导致,但存在指向内存泄漏的疑点,结合细节分析如下:

1. Netty ByteBuf泄漏提示的意义

虽然未直接操作ByteBuf,但WebClient基于Netty实现,该提示说明Netty的内存资源未被正确回收。可能原因包括:

  • WebClient的响应处理存在异常,导致ByteBuf引用未被释放
  • 依赖的studentClient/lectureClient在封装WebClient时存在资源泄漏

2. 多次Full GC后OOM的指向

OOM前多次Full GC,说明堆内存中存在无法被回收的对象(符合内存泄漏特征),但也可能是内存使用量超过堆上限(比如单次chunk处理的数据量过大,或并发请求返回的数据未及时释放)。

3. 代码中的潜在风险

  • ItemTasklet泛型使用Mono<Student>,可能导致chunk中堆积大量未完成的Mono对象,这些对象持有请求上下文、ByteBuf引用等,无法被GC回收
  • write()方法中Flux.concat(chunk.items).collectList().block()会等待所有Mono完成,若chunk size设置过大,会导致大量请求结果堆积在内存中,远超堆容量

下一步排查建议

  • 分析内存快照,查看堆中占比最高的对象类型:如果是Netty的ByteBuf或WebClient上下文对象,可确认资源泄漏;如果是Student或集合对象,可能是chunk size设置不合理
  • 降低chunk size,观察OOM是否复现:若问题缓解,说明是内存过载而非泄漏;若仍出现OOM且ByteBuf泄漏提示存在,可定位到Netty资源泄漏
  • 检查studentClient和lectureClient的实现:确认WebClient的响应是否被正确消费(比如是否存在未处理的异常导致数据流中断)

内容的提问来源于stack exchange,提问作者Sangyoon Jeong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 20:00:24